You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@paimon.apache.org by ni...@apache.org on 2024/01/10 08:37:55 UTC
(incubator-paimon-webui) branch main updated: [Feature] Enhance metadata options module support (#139)
This is an automated email from the ASF dual-hosted git repository.
nicholasjiang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-paimon-webui.git
The following commit(s) were added to refs/heads/main by this push:
new 26e1541 [Feature] Enhance metadata options module support (#139)
26e1541 is described below
commit 26e15415686222294827664665e6bf1c481faad8
Author: xiaomo <we...@gmail.com>
AuthorDate: Wed Jan 10 16:37:50 2024 +0800
[Feature] Enhance metadata options module support (#139)
---
paimon-web-ui-new/mock/modules/metadata.js | 29 ++++
paimon-web-ui-new/package.json | 1 -
paimon-web-ui-new/src/api/models/catalog/index.ts | 46 +++++-
.../src/api/models/catalog/types/table.ts | 6 +-
.../src/components/dynamic-form/index.tsx | 6 +-
.../src/components/dynamic-form/types.ts | 14 +-
.../src/components/dynamic-form/use-form.ts | 7 +-
paimon-web-ui-new/src/components/modal/index.tsx | 8 +-
paimon-web-ui-new/src/form-lib/index.ts | 2 +
.../src/form-lib/metadata/use-options.ts | 65 ++++++++
paimon-web-ui-new/src/store/catalog/index.ts | 4 +-
.../metadata/components/catalog-form/index.tsx | 27 ++--
.../metadata/components/database-form/index.tsx | 7 +-
.../components/menu-tree/index.module.scss | 20 ++-
.../views/metadata/components/menu-tree/index.tsx | 46 +++---
.../metadata/components/metadata-tabs/index.tsx | 3 +-
.../components/options-form-content/index.tsx | 22 +--
.../index.tsx => options-form/edit.tsx} | 61 ++++----
.../metadata/components/options-form/index.tsx | 164 +++++++++++++++++++++
.../views/metadata/components/options/index.tsx | 127 ++++++++++++++++
.../src/views/metadata/components/schema/index.tsx | 35 ++---
.../components/table-column-content/constant.ts | 5 +-
.../components/table-column-content/index.tsx | 70 +++++++--
.../views/metadata/components/table-form/index.tsx | 115 +++++++++++----
.../metadata/{index.module.scss => constant.ts} | 24 ++-
.../src/views/metadata/index.module.scss | 6 +-
paimon-web-ui-new/src/views/metadata/index.tsx | 20 ++-
27 files changed, 744 insertions(+), 196 deletions(-)
diff --git a/paimon-web-ui-new/mock/modules/metadata.js b/paimon-web-ui-new/mock/modules/metadata.js
index 84e8a1e..0f13987 100644
--- a/paimon-web-ui-new/mock/modules/metadata.js
+++ b/paimon-web-ui-new/mock/modules/metadata.js
@@ -105,6 +105,24 @@ module.exports = (mockUtil) => ({
"data": null
}),
+ 'post /metadata/query/options': mockUtil({
+ "code": 200,
+ "msg": "Successfully",
+ "data": [
+ {
+ "key": "bucket",
+ "value": "4"
+ },
+ {
+ "key": "changelog-producer",
+ "value": "input"
+ },
+ {
+ "key": "sink.parallelism",
+ "value": "4"
+ }
+ ]
+}),
'post /metadata/query/schema': mockUtil({
code: 200,
msg: 'Successfully',
@@ -191,5 +209,16 @@ module.exports = (mockUtil) => ({
"creationTime": "@now"
}
]
+ }),
+
+ 'post /table/option/add': mockUtil({
+ code: 200,
+ msg: 'Successfully',
+ data: null,
+ }),
+ 'post /table/option/remove': mockUtil({
+ code: 200,
+ msg: 'Successfully',
+ data: null,
})
})
diff --git a/paimon-web-ui-new/package.json b/paimon-web-ui-new/package.json
index 95d51ca..9ccfd66 100644
--- a/paimon-web-ui-new/package.json
+++ b/paimon-web-ui-new/package.json
@@ -21,7 +21,6 @@
"dayjs": "^1.11.10",
"lodash": "^4.17.21",
"mitt": "^3.0.1",
- "mockm": "1.1.27-alpha.2",
"monaco-editor": "^0.43.0",
"pinia": "^2.1.6",
"pinia-plugin-persistedstate": "^3.2.0",
diff --git a/paimon-web-ui-new/src/api/models/catalog/index.ts b/paimon-web-ui-new/src/api/models/catalog/index.ts
index 268f404..580ca76 100644
--- a/paimon-web-ui-new/src/api/models/catalog/index.ts
+++ b/paimon-web-ui-new/src/api/models/catalog/index.ts
@@ -15,6 +15,8 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
+import type { UseAxleOptions } from '@varlet/axle/use'
+
import httpRequest from '@/api/request'
import type {
Catalog,
@@ -28,7 +30,10 @@ import type {
CatalogDTO,
DatabaseDTO,
TableDTO,
- SearchTable
+ TableParams,
+ SearchTable,
+ OptionsDTO,
+ TableOption
} from './types'
import type { ResponseOptions } from '@/api/types'
@@ -40,7 +45,7 @@ export * from './types'
* # Get all catalog
*/
export const getAllCatalogs = () => {
- return httpRequest.get<unknown, ResponseOptions<Catalog[]>>('/catalog/list')
+ return httpRequest.get<unknown, ResponseOptions<Catalog[]>>('/catalog/getAllCatalogs')
}
/**
@@ -88,12 +93,41 @@ export const createTable = () => {
})
}
+/**
+ * # Get options
+ */
+export const getOptions = () => {
+ return httpRequest.createHooks!<TableOption[], TableParams>({
+ url: '/metadata/query/options',
+ method: 'post',
+ })
+}
+
+/**
+ * # Create new Option
+ */
+export const createOption = () => {
+ return httpRequest.createHooks!<unknown, TableDTO>({
+ url: '/table/option/add',
+ method: 'post'
+ })
+}
+
+/**
+ * # Delete option
+ */
+export const deleteOption = () => {
+ return httpRequest.createHooks!<TableOption[], TableQuery & { key: string; }>({
+ url: '/table/option/remove',
+ method: 'post',
+ })
+}
/**
* # Get schema
*/
export const getSchema = () => {
- return httpRequest.createHooks!<Schema[]>({
+ return httpRequest.createHooks!<Schema[], TableParams>({
url: '/metadata/query/schema',
method: 'post'
})
@@ -103,7 +137,7 @@ export const getSchema = () => {
* # Get manifest
*/
export const getManifest = () => {
- return httpRequest.createHooks!<Manifest[]>({
+ return httpRequest.createHooks!<Manifest[], TableParams>({
url: '/metadata/query/manifest',
method: 'post'
})
@@ -113,7 +147,7 @@ export const getManifest = () => {
* # Get data file
*/
export const getDataFile = () => {
- return httpRequest.createHooks!<Datafile[]>({
+ return httpRequest.createHooks!<Datafile[], TableParams>({
url: '/metadata/query/dataFile',
method: 'post'
})
@@ -123,7 +157,7 @@ export const getDataFile = () => {
* # Get snapshot
*/
export const getSnapshot = () => {
- return httpRequest.createHooks!<Snapshot[]>({
+ return httpRequest.createHooks!<Snapshot[], TableParams>({
url: '/metadata/query/snapshot',
method: 'post'
})
diff --git a/paimon-web-ui-new/src/api/models/catalog/types/table.ts b/paimon-web-ui-new/src/api/models/catalog/types/table.ts
index 887052a..f352f0c 100644
--- a/paimon-web-ui-new/src/api/models/catalog/types/table.ts
+++ b/paimon-web-ui-new/src/api/models/catalog/types/table.ts
@@ -59,7 +59,7 @@ export interface ColumnDTO {
}
export interface DataTypeDTO {
- type: string
+ type?: string
precision?: number | null
scale?: number | null
nullable: boolean
@@ -70,6 +70,6 @@ export interface OptionsDTO {
}
export interface TableOption {
- k: string;
- v: string;
+ key: string;
+ value: string;
}
diff --git a/paimon-web-ui-new/src/components/dynamic-form/index.tsx b/paimon-web-ui-new/src/components/dynamic-form/index.tsx
index 97064a1..a77c8be 100644
--- a/paimon-web-ui-new/src/components/dynamic-form/index.tsx
+++ b/paimon-web-ui-new/src/components/dynamic-form/index.tsx
@@ -15,7 +15,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-import type { FormRules, GridProps, IMeta } from "./types"
+import type { FormRules, GridProps, IFormInst, IMeta } from "./types"
import { useFormHooks } from "./use-form"
const props = {
@@ -36,11 +36,11 @@ const props = {
export default defineComponent({
name: 'DynamicForm',
props,
- setup(props, { expose }) {
+ setup(_, { expose }) {
const { state, ...rest } = useFormHooks()
expose({
...rest
- })
+ } as IFormInst)
return { ...toRefs(state) }
},
render(props: { meta: IMeta; gridProps?: GridProps; loading?: boolean }) {
diff --git a/paimon-web-ui-new/src/components/dynamic-form/types.ts b/paimon-web-ui-new/src/components/dynamic-form/types.ts
index 56cd0e9..231a9a5 100644
--- a/paimon-web-ui-new/src/components/dynamic-form/types.ts
+++ b/paimon-web-ui-new/src/components/dynamic-form/types.ts
@@ -15,7 +15,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-import type { FormItemRule, FormProps, FormRules, GridProps } from "naive-ui"
+import type { FormInst, FormItemRule, FormProps, FormRules, GridProps } from "naive-ui"
type ComponentType =
| 'input'
@@ -71,6 +71,15 @@ interface IMeta extends Omit<FormProps, 'model' | 'rules'> {
rules: IFormRules
}
+interface IFormInst extends FormInst {
+ setValues: (initialValues: { [field: string]: any }) => void
+ resetValues: (initialValues: { [field: string]: any }) => void
+ getValues: () => { [field: string]: any }
+ restoreValidation: () => void
+ validate: (...args: []) => Promise<any>
+ formatValidate: (validate?: IFormItemRule | FormRules) => IFormItemRule
+}
+
export type {
IJsonItem,
IJsonItemParams,
@@ -82,5 +91,6 @@ export type {
IFormItemRule,
IMeta,
GridProps,
- FormRules
+ FormRules,
+ IFormInst
}
diff --git a/paimon-web-ui-new/src/components/dynamic-form/use-form.ts b/paimon-web-ui-new/src/components/dynamic-form/use-form.ts
index e508191..7c51a68 100644
--- a/paimon-web-ui-new/src/components/dynamic-form/use-form.ts
+++ b/paimon-web-ui-new/src/components/dynamic-form/use-form.ts
@@ -15,15 +15,16 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-import type { FormRules, IFormItemRule } from "./types"
+import type { FormInst } from 'naive-ui'
+import type { IFormInst, FormRules, IFormItemRule } from './types'
-export const useFormHooks = () => {
+export const useFormHooks = (): IFormInst & { state: { formRef: FormInst } } => {
const state = reactive({
formRef: ref()
})
const validate = async (...args: []) => {
- await state.formRef.validate(...args)
+ return state.formRef.validate(...args)
}
const setValues = (initialValues: { [field: string]: any }) => {
diff --git a/paimon-web-ui-new/src/components/modal/index.tsx b/paimon-web-ui-new/src/components/modal/index.tsx
index a781e57..9f0d3a8 100644
--- a/paimon-web-ui-new/src/components/modal/index.tsx
+++ b/paimon-web-ui-new/src/components/modal/index.tsx
@@ -15,10 +15,10 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-import type { FormInst } from 'naive-ui'
+import Form from '@/components/dynamic-form'
+import type { IFormInst } from '@/components/dynamic-form/types'
-import Form from "@/components/dynamic-form"
-import { useTask } from "./use-task"
+import { useTask } from './use-task'
const props = {
title: {
@@ -53,7 +53,7 @@ export default defineComponent({
emits: ['confirm', 'cancel'],
setup(props, { expose, emit }) {
const { t } = useLocaleHooks()
- const formRef = ref<FormInst | null>()
+ const formRef = ref<IFormInst | null>()
expose({ formRef })
const { elementsRef, rulesRef, model } = useTask({
diff --git a/paimon-web-ui-new/src/form-lib/index.ts b/paimon-web-ui-new/src/form-lib/index.ts
index bea031f..18a3d2c 100644
--- a/paimon-web-ui-new/src/form-lib/index.ts
+++ b/paimon-web-ui-new/src/form-lib/index.ts
@@ -20,10 +20,12 @@ import { useMYSQL } from './cdc/use-mysql'
import { usePaimon } from './cdc/use-paimon'
import { useDatabase } from './metadata/use-database'
+import { useOptions } from './metadata/use-options'
export default {
CDCLIST: useCDCList,
MYSQL: useMYSQL,
PAIMON: usePaimon,
DATABASE: useDatabase,
+ OPTIONS: useOptions
}
diff --git a/paimon-web-ui-new/src/form-lib/metadata/use-options.ts b/paimon-web-ui-new/src/form-lib/metadata/use-options.ts
new file mode 100644
index 0000000..85daf1f
--- /dev/null
+++ b/paimon-web-ui-new/src/form-lib/metadata/use-options.ts
@@ -0,0 +1,65 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import type { IJsonItem } from '@/components/dynamic-form/types'
+import type { TableOption } from '@/api/models/catalog'
+
+export function useOptions({ data }: { data?: TableOption }) {
+ const model = reactive(data ? data : { key: '', value: '' })
+
+ return {
+ json: [
+ {
+ type: 'input',
+ field: 'key',
+ name: 'Key',
+ props: {
+ placeholder: 'Key',
+ disabled: true,
+ },
+ validate: {
+ trigger: ['input', 'blur'],
+ required: true,
+ message: 'error',
+ validator: (validator: any, value: string) => {
+ if (!value) {
+ return new Error('Key is required')
+ }
+ }
+ }
+ },
+ {
+ type: 'input',
+ field: 'value',
+ name: 'Value',
+ props: {
+ placeholder: 'Value',
+ },
+ validate: {
+ trigger: ['input', 'blur'],
+ required: true,
+ message: 'error',
+ validator: (validator: any, value: string) => {
+ if (!value) {
+ return new Error('Value is required')
+ }
+ }
+ }
+ },
+ ] as IJsonItem[], model
+ }
+}
diff --git a/paimon-web-ui-new/src/store/catalog/index.ts b/paimon-web-ui-new/src/store/catalog/index.ts
index 089c1c6..fc34bfa 100644
--- a/paimon-web-ui-new/src/store/catalog/index.ts
+++ b/paimon-web-ui-new/src/store/catalog/index.ts
@@ -93,7 +93,7 @@ const transformDatabase = (databases: Database[]): TreeOption[] => {
return databases.map(database => ({
label: database.name,
type: 'database',
- key: `${database.catalogId} ${database.name}`,
+ key: `${database.catalogId} ${database.catalogName} ${database.name}`,
isLeaf: false,
}))
}
@@ -102,7 +102,7 @@ const transformTable = (tables: Table[]): TreeOption[] => {
return tables.map(table => ({
label: table.name,
type: 'table',
- key: `${table.catalogId} ${table.catalogName} ${table.databaseName} ${table.name}`,
+ key: `${JSON.stringify(table)}`,
isLeaf: true,
prefix: () =>
h(NIcon, null, {
diff --git a/paimon-web-ui-new/src/views/metadata/components/catalog-form/index.tsx b/paimon-web-ui-new/src/views/metadata/components/catalog-form/index.tsx
index bccd693..279bf50 100644
--- a/paimon-web-ui-new/src/views/metadata/components/catalog-form/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/components/catalog-form/index.tsx
@@ -91,17 +91,7 @@ export default defineComponent({
if (result.value.code === 200) {
handleCloseModal()
message.success(t('Create Successfully'))
- formValue.value = {
- name: '',
- type: '',
- warehouse: '',
- options: {
- fileSystemType: 'local',
- endpoint: '',
- accessKey: '',
- secretKey: ''
- }
- }
+ resetState()
catalogStore.getAllCatalogs(true)
}
}
@@ -113,6 +103,21 @@ export default defineComponent({
const handleCloseModal = () => {
showModal.value = false
+ resetState()
+ }
+
+ const resetState = () => {
+ formValue.value = {
+ name: '',
+ type: '',
+ warehouse: '',
+ options: {
+ fileSystemType: 'local',
+ endpoint: '',
+ accessKey: '',
+ secretKey: ''
+ }
+ }
}
return {
diff --git a/paimon-web-ui-new/src/views/metadata/components/database-form/index.tsx b/paimon-web-ui-new/src/views/metadata/components/database-form/index.tsx
index 99e4c74..f054759 100644
--- a/paimon-web-ui-new/src/views/metadata/components/database-form/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/components/database-form/index.tsx
@@ -16,11 +16,11 @@ specific language governing permissions and limitations
under the License. */
import { Add } from '@vicons/ionicons5'
-import type { FormInst } from 'naive-ui'
import { createDatabase, type DatabaseFormDTO } from '@/api/models/catalog'
import { useCatalogStore } from '@/store/catalog'
import IModal from '@/components/modal'
+import type { IFormInst } from '@/components/dynamic-form/types'
const props = {
catalogId: {
@@ -39,7 +39,7 @@ export default defineComponent({
const catalogStore = useCatalogStore()
const [result, createFetch, { loading }] = createDatabase()
- const modalRef = ref<{ formRef: FormInst }>()
+ const modalRef = ref<{ formRef: IFormInst }>()
const showModal = ref(false)
const handleConfirm = async (values: DatabaseFormDTO) => {
@@ -70,6 +70,9 @@ export default defineComponent({
const handleCloseModal = () => {
showModal.value = false
+ modalRef.value?.formRef?.resetValues({
+ name: '',
+ })
}
return {
diff --git a/paimon-web-ui-new/src/views/metadata/components/menu-tree/index.module.scss b/paimon-web-ui-new/src/views/metadata/components/menu-tree/index.module.scss
index 8926adc..9c58b31 100644
--- a/paimon-web-ui-new/src/views/metadata/components/menu-tree/index.module.scss
+++ b/paimon-web-ui-new/src/views/metadata/components/menu-tree/index.module.scss
@@ -24,12 +24,24 @@ under the License. */
.card {
height: 100%;
- .search {
+ .vertical {
display: flex;
- }
+ flex-direction: column;
+ height: 100%;
+ gap: 20px;
- .icon {
- display: flex;
+ .search {
+ display: flex;
+ }
+
+ .icon {
+ display: flex;
+ }
+
+ .scroll {
+ position: relative;
+ flex: 1;
+ }
}
}
}
diff --git a/paimon-web-ui-new/src/views/metadata/components/menu-tree/index.tsx b/paimon-web-ui-new/src/views/metadata/components/menu-tree/index.tsx
index 0b9e189..c1060bb 100644
--- a/paimon-web-ui-new/src/views/metadata/components/menu-tree/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/components/menu-tree/index.tsx
@@ -57,7 +57,7 @@ export default defineComponent({
return h(DatabaseFormButton, { catalogId: Number(catalogId) })
case 'database':
const [id, name, databaseName] = option.key?.toString()?.split(' ') || []
- return h(TableFormButton, { catalogId: id, catalogName: name, databaseName })
+ return h(TableFormButton, { catalogId: Number(id), catalogName: name, databaseName })
default:
return undefined
}
@@ -83,9 +83,10 @@ export default defineComponent({
if (node.type === 'catalog') {
node.children = await catalogStore.getDatabasesById(node.key as number)
} else {
- const [catalogId, databaseName] = (node.key as string)?.split(' ') || []
+ const [catalogId, catalogName, databaseName] = (node.key as string)?.split(' ') || []
const params = {
catalogId: Number(catalogId),
+ catalogName,
databaseName
}
node.children = (await catalogStore.getTablesByDataBaseId(params)) || []
@@ -99,13 +100,12 @@ export default defineComponent({
onClick() {
const { type } = option
if (type === 'table') {
- const [catalogId, catalogName, databaseName, name] =
- (option.key?.toString() || '')?.split(' ') || []
+ const { catalogId, name, ...tableData } = JSON.parse(option.key?.toString() || '')
catalogStore.setCurrentTable({
catalogId: Number(catalogId),
- catalogName,
- databaseName,
- tableName: name
+ tableName: name,
+ name,
+ ...tableData
})
}
}
@@ -139,7 +139,7 @@ export default defineComponent({
return (
<div class={styles.container}>
<n-card class={styles.card} content-style={'padding:20px 18px;'}>
- <n-space vertical>
+ <div class={styles.vertical}>
<n-space justify="space-between" align="enter">
<article>Catalog</article>
<CatalogFormButton />
@@ -153,19 +153,23 @@ export default defineComponent({
}}
onKeyup={this.onSearch}
></n-input>
- <n-spin show={this.menuLoading}>
- <n-tree
- block-line
- expand-on-click
- data={this.menuList}
- defaultExpandAll={this.isSearch}
- nodeProps={this.nodeProps}
- renderSuffix={this.renderSuffix}
- renderSwitcherIcon={this.renderPrefix}
- onLoad={this.onLoadMenu}
- />
- </n-spin>
- </n-space>
+ <div class={styles.scroll}>
+ <n-scrollbar style={'position: absolute'}>
+ <n-spin show={this.menuLoading}>
+ <n-tree
+ block-line
+ expand-on-click
+ data={this.menuList}
+ defaultExpandAll={this.isSearch}
+ nodeProps={this.nodeProps}
+ renderSuffix={this.renderSuffix}
+ renderSwitcherIcon={this.renderPrefix}
+ onLoad={this.onLoadMenu}
+ />
+ </n-spin>
+ </n-scrollbar>
+ </div>
+ </div>
</n-card>
</div>
)
diff --git a/paimon-web-ui-new/src/views/metadata/components/metadata-tabs/index.tsx b/paimon-web-ui-new/src/views/metadata/components/metadata-tabs/index.tsx
index 6704ce0..027269d 100644
--- a/paimon-web-ui-new/src/views/metadata/components/metadata-tabs/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/components/metadata-tabs/index.tsx
@@ -20,6 +20,7 @@ import type { TabsInst } from 'naive-ui'
import { useConfigStore } from '@/store/config'
import Table from '../table'
+import Options from '../options'
import Schema from '../schema'
import SnapShot from '../snapshot'
import Manifest from '../manifest'
@@ -52,7 +53,7 @@ export default defineComponent({
<Table />
</n-tab-pane>
<n-tab-pane name='option' tab={this.t('metadata.option_info')}>
- Option
+ <Options />
</n-tab-pane>
<n-tab-pane name='schema' tab={this.t('metadata.schema_info')}>
<Schema />
diff --git a/paimon-web-ui-new/src/views/metadata/components/options-form-content/index.tsx b/paimon-web-ui-new/src/views/metadata/components/options-form-content/index.tsx
index 051f633..44ad630 100644
--- a/paimon-web-ui-new/src/views/metadata/components/options-form-content/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/components/options-form-content/index.tsx
@@ -15,6 +15,8 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
+import { RemoveCircleOutline } from '@vicons/ionicons5'
+
import type { TableOption } from '@/api/models/catalog'
const props = {
@@ -26,8 +28,8 @@ const props = {
}
export const newOption: TableOption = {
- k: '',
- v: ''
+ key: '',
+ value: ''
}
export default defineComponent({
@@ -53,30 +55,32 @@ export default defineComponent({
<n-form-item-gi
span={11}
showLabel={false}
- path={`options[${index}].k`}
+ path={`options[${index}].key`}
rule={{
required: true,
message: 'Key is required',
trigger: ['input', 'blur']
}}
>
- <n-input v-model:value={item.k} placeholder="Key" />
+ <n-input v-model:value={item.key} placeholder="Key" />
</n-form-item-gi>
<n-form-item-gi
span={11}
showLabel={false}
- path={`options[${index}].v`}
+ path={`options[${index}].value`}
rule={{
required: true,
message: 'Value is required',
trigger: ['input', 'blur']
}}
>
- <n-input v-model:value={item.v} placeholder="Value" />
+ <n-input v-model:value={item.value} placeholder="Value" />
</n-form-item-gi>
- <n-gi suffix block span={2} class="suffix">
- <n-button onClick={() => this.onDelete(index)} tertiary type="error">
- Remove
+ <n-gi span={2}>
+ <n-button block onClick={() => this.onDelete(index)} tertiary type="error">
+ {{
+ icon: () => <n-icon component={RemoveCircleOutline} />
+ }}
</n-button>
</n-gi>
</>
diff --git a/paimon-web-ui-new/src/views/metadata/components/database-form/index.tsx b/paimon-web-ui-new/src/views/metadata/components/options-form/edit.tsx
similarity index 58%
copy from paimon-web-ui-new/src/views/metadata/components/database-form/index.tsx
copy to paimon-web-ui-new/src/views/metadata/components/options-form/edit.tsx
index 99e4c74..8443161 100644
--- a/paimon-web-ui-new/src/views/metadata/components/database-form/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/components/options-form/edit.tsx
@@ -15,51 +15,45 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-import { Add } from '@vicons/ionicons5'
-import type { FormInst } from 'naive-ui'
+import { CreateOutline } from '@vicons/ionicons5'
-import { createDatabase, type DatabaseFormDTO } from '@/api/models/catalog'
+import { createOption, type TableOption } from '@/api/models/catalog'
import { useCatalogStore } from '@/store/catalog'
import IModal from '@/components/modal'
+import type { IFormInst } from '@/components/dynamic-form/types'
+import { transformOption } from '@/views/metadata/constant'
const props = {
- catalogId: {
- type: Number as PropType<number>,
- require: true
- }
+ value: Object as PropType<TableOption>,
+ onConfirm: [Function, Array] as PropType<() => Promise<void>>
}
export default defineComponent({
- name: 'DatabaseForm',
+ name: 'OptionEditForm',
props,
setup(props) {
const { t } = useLocaleHooks()
const message = useMessage()
const catalogStore = useCatalogStore()
- const [result, createFetch, { loading }] = createDatabase()
+ const [result, createFetch, { loading }] = createOption()
- const modalRef = ref<{ formRef: FormInst }>()
+ const modalRef = ref<{ formRef: IFormInst }>()
const showModal = ref(false)
- const handleConfirm = async (values: DatabaseFormDTO) => {
+ const handleConfirm = async (values: TableOption) => {
await modalRef.value?.formRef?.validate()
- if (props.catalogId) {
- await createFetch({
- params: {
- ...values,
- catalogId: props.catalogId
- }
+ await createFetch({
+ params: transformOption({
+ ...toRaw(catalogStore.currentTable),
+ options: [toRaw(values)]
})
+ })
- if (result.value.code === 200) {
- handleCloseModal()
- message.success(t('Create Successfully'))
- values = reactive({
- name: '',
- })
- catalogStore.getAllCatalogs(true)
- }
+ if (result.value.code === 200) {
+ handleCloseModal()
+ message.success(t('Create Successfully'))
+ props.onConfirm!()
}
}
@@ -70,11 +64,13 @@ export default defineComponent({
const handleCloseModal = () => {
showModal.value = false
+ props.onConfirm!()
}
return {
modalRef,
showModal,
+ value: toRefs(props).value,
t,
handleOpenModal,
@@ -85,16 +81,17 @@ export default defineComponent({
render() {
return (
<>
- <n-button quaternary circle size="tiny" onClick={this.handleOpenModal}>
- <n-icon>
- <Add />
- </n-icon>
+ <n-button onClick={this.handleOpenModal} strong secondary circle>
+ {{
+ icon: () => <n-icon component={CreateOutline} />
+ }}
</n-button>
<IModal
- ref="modalRef"
+ ref='modalRef'
showModal={this.showModal}
- title={this.t(`metadata.create_database`)}
- formType={'DATABASE'}
+ row={this.value}
+ title={'Edit Option'}
+ formType={'OPTIONS'}
onCancel={this.handleCloseModal}
onConfirm={this.handleConfirm}
/>
diff --git a/paimon-web-ui-new/src/views/metadata/components/options-form/index.tsx b/paimon-web-ui-new/src/views/metadata/components/options-form/index.tsx
new file mode 100644
index 0000000..8323049
--- /dev/null
+++ b/paimon-web-ui-new/src/views/metadata/components/options-form/index.tsx
@@ -0,0 +1,164 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import { Add, AddCircleOutline } from '@vicons/ionicons5'
+
+import { useCatalogStore } from '@/store/catalog'
+import { createOption, type TableOption } from '@/api/models/catalog'
+import { transformOption } from '@/views/metadata/constant'
+
+import OptionContent, { newOption } from '../options-form-content'
+
+type OptionsFormType = {
+ options: TableOption[]
+}
+
+const props = {
+ onConfirm: [Function, Array] as PropType<() => Promise<void>>
+}
+
+export default defineComponent({
+ name: 'OptionForm',
+ props,
+ setup(props) {
+ const rules = {
+ key: {
+ required: true,
+ trigger: ['blur', 'input'],
+ message: 'Option key required'
+ },
+ value: {
+ required: true,
+ trigger: ['blur', 'input'],
+ message: 'Option value required'
+ }
+ }
+
+ const { t } = useLocaleHooks()
+ const message = useMessage()
+
+ const catalogStore = useCatalogStore()
+ const [result, createFetch, { loading }] = createOption()
+
+ const formRef = ref()
+ const formValue = ref<OptionsFormType>({
+ options: [{ ...newOption }]
+ })
+ const showModal = ref(false)
+
+ const handleConfirm = async () => {
+ await formRef.value.validate()
+ await createFetch({
+ params: transformOption({
+ ...toRaw(catalogStore.currentTable),
+ options: toRaw(formValue.value).options
+ })
+ })
+
+ if (result.value.code === 200) {
+ handleCloseModal()
+ message.success(t('Create Successfully'))
+ resetState()
+ props.onConfirm!()
+ }
+ }
+
+ const handleOpenModal = (e: Event) => {
+ e.stopPropagation()
+ showModal.value = true
+ }
+
+ const handleCloseModal = () => {
+ showModal.value = false
+ resetState()
+ }
+
+ const resetState = () => {
+ formValue.value = {
+ options: [{ ...newOption }]
+ }
+ }
+
+ const handleAddOption = () => {
+ formValue.value?.options.push({ ...newOption })
+ }
+
+ return {
+ formRef,
+ formValue,
+ showModal,
+ loading,
+
+ rules,
+
+ t,
+ handleOpenModal,
+ handleCloseModal,
+ handleConfirm,
+ handleAddOption
+ }
+ },
+ render() {
+ return (
+ <>
+ <n-button strong secondary circle onClick={this.handleOpenModal}>
+ {{
+ icon: () => <n-icon component={AddCircleOutline} />
+ }}
+ </n-button>
+ <n-modal v-model:show={this.showModal} mask-closable={false}>
+ <n-card bordered={true} title={'Create Option'} style="width: 700px">
+ {{
+ 'header-extra': () => (
+ <n-button quaternary circle size="tiny" onClick={this.handleAddOption}>
+ <n-icon>
+ <Add />
+ </n-icon>
+ </n-button>
+ ),
+ default: () => (
+ <n-form
+ ref="formRef"
+ label-placement="top"
+ label-width="auto"
+ label-align="left"
+ rules={this.rules}
+ model={this.formValue}
+ >
+ <OptionContent
+ onUpdateOptions={(value) => {
+ this.formValue.options = value
+ }}
+ options={this.formValue.options || []}
+ />
+ </n-form>
+ ),
+ action: () => (
+ <n-space justify="end">
+ <n-button onClick={this.handleCloseModal}>{this.t('layout.cancel')}</n-button>
+ <n-button type="primary" loading={this.loading} onClick={this.handleConfirm}>
+ {this.t('layout.confirm')}
+ </n-button>
+ </n-space>
+ )
+ }}
+ </n-card>
+ </n-modal>
+ </>
+ )
+ }
+})
diff --git a/paimon-web-ui-new/src/views/metadata/components/options/index.tsx b/paimon-web-ui-new/src/views/metadata/components/options/index.tsx
new file mode 100644
index 0000000..1c53f82
--- /dev/null
+++ b/paimon-web-ui-new/src/views/metadata/components/options/index.tsx
@@ -0,0 +1,127 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import { type DataTableColumns } from 'naive-ui'
+import { RemoveCircleOutline, Warning } from '@vicons/ionicons5'
+
+import { deleteOption, getOptions, type TableOption } from '@/api/models/catalog'
+import { useCatalogStore } from '@/store/catalog'
+import OptionsForm from '../options-form'
+import OptionsEditForm from '../options-form/edit'
+
+export default defineComponent({
+ name: 'MetadataOptions',
+ setup() {
+ const { t } = useLocaleHooks()
+ const catalogStore = useCatalogStore()
+
+ const [optionsList, useOptionsList, { loading }] = getOptions()
+ const [, useDelete] = deleteOption()
+
+ const columns: DataTableColumns<TableOption> = [
+ {
+ title: 'Key',
+ key: 'key'
+ },
+ {
+ title: 'Value',
+ key: 'value'
+ },
+ {
+ title: 'Operation',
+ key: 'operation',
+ render(rowData) {
+ return (
+ <n-space>
+ <OptionsEditForm onConfirm={onFetchData} value={rowData} />
+ <n-popconfirm onPositiveClick={() => onDeleteOption(rowData?.key)}>
+ {{
+ default: () => 'Confirm to delete ? ',
+ trigger: () => (
+ <n-button strong secondary circle type="error">
+ {{
+ icon: () => <n-icon component={RemoveCircleOutline} />
+ }}
+ </n-button>
+ ),
+ icon: () => <n-icon color='#EC4C4D' component={Warning} />
+ }}
+ </n-popconfirm>
+ </n-space>
+ )
+ }
+ }
+ ]
+
+ const onFetchData = async () => {
+ useOptionsList({
+ params: catalogStore.currentTable
+ })
+ }
+
+ const onDeleteOption = async (optionKey: string) => {
+ await useDelete({
+ config: {
+ params: {
+ ...toRaw(catalogStore.currentTable),
+ key: optionKey
+ }
+ }
+ })
+
+ await onFetchData()
+
+ return Promise.resolve(true)
+ }
+
+ watch(() => catalogStore.currentTable, onFetchData)
+
+ onMounted(onFetchData)
+
+ return {
+ columns,
+ optionsList,
+ loading,
+ pagination: {
+ pageSize: 10
+ },
+
+ onFetchData,
+ t
+ }
+ },
+ render() {
+ return (
+ <n-spin show={this.loading}>
+ <n-card title="Options">
+ {{
+ 'header-extra': () => (
+ <OptionsForm onConfirm={this.onFetchData} />
+ ),
+ default: () => (
+ <n-data-table
+ columns={this.columns}
+ data={this.optionsList?.data || []}
+ pagination={this.pagination}
+ />
+ )
+ }}
+ </n-card>
+ </n-spin>
+ )
+ }
+})
diff --git a/paimon-web-ui-new/src/views/metadata/components/schema/index.tsx b/paimon-web-ui-new/src/views/metadata/components/schema/index.tsx
index faa2b41..bc0aa04 100644
--- a/paimon-web-ui-new/src/views/metadata/components/schema/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/components/schema/index.tsx
@@ -28,7 +28,7 @@ export default defineComponent({
name: 'MetadataSchema',
setup() {
const { t } = useLocaleHooks()
-
+
const catalogStore = useCatalogStore()
const [schemaData, useSchemaInfo, { loading }] = getSchema()
@@ -40,25 +40,19 @@ export default defineComponent({
renderExpand: (row) => {
const { fields = [], option = [] } = row
- return <div>
- <div class={styles.schema_field_title}>Fields: </div>
- <n-data-table
- columns={fieldsColumns}
- data={fields}
- bordered={false}
- />
- <div class={styles.schema_field_title}>Options: </div>
- <n-data-table
- columns={optionsColumns}
- data={option}
- bordered={false}
- />
- </div>
+ return (
+ <div>
+ <div class={styles.schema_field_title}>Fields: </div>
+ <n-data-table columns={fieldsColumns} data={fields} bordered={false} />
+ <div class={styles.schema_field_title}>Options: </div>
+ <n-data-table columns={optionsColumns} data={option} bordered={false} />
+ </div>
+ )
}
},
{
title: 'ID',
- key: 'schemaId',
+ key: 'schemaId'
},
{
title: 'Partition Keys',
@@ -99,13 +93,12 @@ export default defineComponent({
columns,
schemaData,
loading,
- t,
+ t
}
},
render() {
return (
<n-card>
- {this.loading}
<n-spin show={this.loading}>
<n-data-table
row-key={(rowData: Schema) => rowData.schemaId}
@@ -115,6 +108,6 @@ export default defineComponent({
/>
</n-spin>
</n-card>
- );
- },
-});
+ )
+ }
+})
diff --git a/paimon-web-ui-new/src/views/metadata/components/table-column-content/constant.ts b/paimon-web-ui-new/src/views/metadata/components/table-column-content/constant.ts
index ebf27a4..430ae20 100644
--- a/paimon-web-ui-new/src/views/metadata/components/table-column-content/constant.ts
+++ b/paimon-web-ui-new/src/views/metadata/components/table-column-content/constant.ts
@@ -15,7 +15,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-export const options = [
+export const dataTypeOptions = [
{ value: 'INT', label: 'INT' },
{ value: 'TINYINT', label: 'TINYINT' },
{ value: 'SMALLINT', label: 'SMALLINT' },
@@ -40,3 +40,6 @@ export const options = [
{ value: 'TIMESTAMP_WITH_LOCAL_TIME_ZONE(precision)', label: 'TIMESTAMP_WITH_LOCAL_TIME_ZONE(precision)' },
];
+export const hasLength = ['CHAR', 'VARCHAR', 'BINARY', 'VARBINARY', 'TIME(precision)', 'TIMESTAMP(precision)', 'TIMESTAMP_WITH_LOCAL_TIME_ZONE(precision)', 'DECIMAL'];
+
+export const hasEndLength = ['DECIMAL'];
diff --git a/paimon-web-ui-new/src/views/metadata/components/table-column-content/index.tsx b/paimon-web-ui-new/src/views/metadata/components/table-column-content/index.tsx
index e1c2352..8975b0d 100644
--- a/paimon-web-ui-new/src/views/metadata/components/table-column-content/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/components/table-column-content/index.tsx
@@ -19,6 +19,8 @@ import type { DataTableColumns } from 'naive-ui'
import type { ColumnDTO } from '@/api/models/catalog'
+import { hasLength, hasEndLength, dataTypeOptions } from './constant'
+
const props = {
data: {
type: Array as PropType<ColumnDTO[]>,
@@ -27,6 +29,17 @@ const props = {
onUpdateColumns: [Function, Array] as PropType<((value: ColumnDTO[]) => void) | undefined>
}
+export const newField: ColumnDTO = {
+ field: '',
+ dataType: {
+ nullable: true,
+ type: undefined
+ },
+ comment: '',
+ defaultValue: '',
+ pk: false
+}
+
export default defineComponent({
name: 'OptionFormContent',
props,
@@ -80,7 +93,11 @@ export default defineComponent({
trigger: ['input', 'blur']
}}
>
- <n-select v-model:value={row.dataType.type} />
+ <n-select
+ placeholder={t('metadata.column_type')}
+ v-model:value={row.dataType.type}
+ options={dataTypeOptions}
+ />
</n-form-item>
)
}
@@ -88,20 +105,41 @@ export default defineComponent({
{
title: t('metadata.column_length'),
key: 'length',
- width: 150,
+ width: 190,
render: (row, index) => {
+ const hasStartLength = hasLength.includes(row.dataType.type || '')
+ const hasScaleLength = hasEndLength.includes(row.dataType.type || '')
return (
- <n-form-item
- show-feedback={false}
- showLabel={false}
- path={`tableColumns[${index}].dataType`}
- rule={{
- required: true,
- trigger: ['input', 'blur']
- }}
- >
- <n-select v-model:value={row.dataType} />
- </n-form-item>
+ <n-space wrap={false}>
+ {hasStartLength && (
+ <n-form-item
+ show-feedback={false}
+ showLabel={false}
+ path={`tableColumns[${index}].dataType.precision`}
+ rule={{
+ type: 'number',
+ required: true,
+ trigger: ['input', 'blur']
+ }}
+ >
+ <n-input-number v-model:value={row.dataType.precision} show-button={false} />
+ </n-form-item>
+ )}
+ {hasScaleLength && (
+ <n-form-item
+ show-feedback={false}
+ showLabel={false}
+ path={`tableColumns[${index}].dataType.scale`}
+ rule={{
+ type: 'number',
+ required: true,
+ trigger: ['input', 'blur']
+ }}
+ >
+ <n-input-number v-model:value={row.dataType.scale} show-button={false} />
+ </n-form-item>
+ )}
+ </n-space>
)
}
},
@@ -112,14 +150,14 @@ export default defineComponent({
render: (row, index) => {
return (
<n-form-item show-feedback={false} showLabel={false} path={`tableColumns[${index}].pk`}>
- <n-checkbox v-model:value={row.pk} />
+ <n-checkbox v-model:checked={row.pk} />
</n-form-item>
)
}
},
{
title: t('metadata.column_nullable'),
- key: 'nullable',
+ key: 'dataType.nullable',
width: 80,
render: (row, index) => {
return (
@@ -128,7 +166,7 @@ export default defineComponent({
showLabel={false}
path={`tableColumns[${index}].dataType.nullable`}
>
- <n-checkbox v-model:value={row.dataType.nullable} />
+ <n-checkbox v-model:checked={row.dataType.nullable} />
</n-form-item>
)
}
diff --git a/paimon-web-ui-new/src/views/metadata/components/table-form/index.tsx b/paimon-web-ui-new/src/views/metadata/components/table-form/index.tsx
index 6044a86..182d6dc 100644
--- a/paimon-web-ui-new/src/views/metadata/components/table-form/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/components/table-form/index.tsx
@@ -18,16 +18,54 @@ under the License. */
import { Add } from '@vicons/ionicons5'
import { useCatalogStore } from '@/store/catalog'
-import { createTable, type TableDTO, type TableOption } from '@/api/models/catalog'
+import { createTable, type TableDTO } from '@/api/models/catalog'
+import { transformOption } from '@/views/metadata/constant'
import OptionContent, { newOption } from '../options-form-content'
-import TableColumnContent from '../table-column-content'
+import TableColumnContent, { newField } from '../table-column-content'
import styles from './index.module.scss'
+
+const props = {
+ catalogId: {
+ type: Number as PropType<number>,
+ require: true
+ },
+ catalogName: {
+ type: String as PropType<string>,
+ require: true
+ },
+ databaseName: {
+ type: String as PropType<string>,
+ require: true
+ }
+}
+
+
+const resetFormValue = () => {
+ return {
+ name: '',
+ tableColumns: [
+ {
+ field: '',
+ dataType: {
+ nullable: true,
+ type: undefined
+ },
+ comment: '',
+ defaultValue: '',
+ pk: false
+ }
+ ],
+ options: []
+ }
+}
+
export default defineComponent({
name: 'TableForm',
- setup() {
+ props,
+ setup(props) {
const rules = {}
const { t } = useLocaleHooks()
@@ -37,37 +75,35 @@ export default defineComponent({
const [result, createFetch, { loading }] = createTable()
const formRef = ref()
- const formValue = ref<TableDTO>({
- name: '',
- tableColumns: [
- {
- field: '',
- dataType: {
- type: '',
- nullable: true
- },
- comment: '',
- defaultValue: '',
- pk: false
- }
- ],
- options: []
- })
+ const formValue = ref<TableDTO>(resetFormValue())
const showModal = ref(false)
+ const tableKeys = computed(() => {
+ return formValue.value
+ .tableColumns!.filter((item) => Boolean(item.field))
+ .map((item) => {
+ return {
+ label: item.field,
+ value: item.field
+ }
+ })
+ })
+
const handleConfirm = async () => {
await formRef.value.validate()
+
await createFetch({
- params: toRaw(formValue.value)
+ params: {
+ ...toRaw(props),
+ ...transformOption(toRaw(formValue.value))
+ }
})
if (result.value.code === 200) {
handleCloseModal()
message.success(t('Create Successfully'))
- formValue.value = {
- name: ''
- }
- catalogStore.getAllCatalogs(true)
+ formValue.value = resetFormValue()
+ await catalogStore.getAllCatalogs(true)
}
}
@@ -78,25 +114,33 @@ export default defineComponent({
const handleCloseModal = () => {
showModal.value = false
+ formValue.value = resetFormValue()
}
const handleAddOption = () => {
formValue.value.options?.push({ ...newOption })
}
+ const handleAddColumn = () => {
+ formValue.value.tableColumns?.push(JSON.parse(JSON.stringify(newField)))
+ }
+
+
return {
formRef,
formValue,
showModal,
loading,
+ tableKeys,
rules,
t,
handleOpenModal,
handleCloseModal,
handleConfirm,
- handleAddOption
+ handleAddOption,
+ handleAddColumn
}
},
render() {
@@ -113,16 +157,24 @@ export default defineComponent({
default: () => (
<n-form ref="formRef" rules={this.rules} model={this.formValue}>
<div class={styles.form_title}>{this.t('metadata.table_basic_information')}</div>
- <n-form-item label={this.t('metadata.table_name')} path="name">
+ <n-form-item
+ rule={{
+ required: true,
+ message: 'Name is required',
+ trigger: ['input', 'blur']
+ }}
+ label={this.t('metadata.table_name')}
+ path="name"
+ >
<n-input v-model:value={this.formValue.name} />
</n-form-item>
<n-form-item label={this.t('metadata.table_des')} path="type">
- <n-input type="textarea" />
+ <n-input v-model:value={this.formValue.description} type="textarea" />
</n-form-item>
<n-space align="center" justify="space-between" class={styles.form_title}>
{this.t('metadata.table_columns')}
- <n-button circle onClick={this.handleOpenModal}>
+ <n-button circle onClick={this.handleAddColumn}>
<n-icon>
<Add />
</n-icon>
@@ -136,7 +188,12 @@ export default defineComponent({
/>
<div class={styles.form_title}>{this.t('metadata.partition_columns')}</div>
<n-form-item showLabel={false} path="type">
- <n-select placeholder="Select" />
+ <n-select
+ multiple
+ options={this.tableKeys}
+ v-model:value={this.formValue.partitionKey}
+ placeholder="Select"
+ />
</n-form-item>
<n-space align="center" justify="space-between" class={styles.form_title}>
{this.t('metadata.table_add_options')}
diff --git a/paimon-web-ui-new/src/views/metadata/index.module.scss b/paimon-web-ui-new/src/views/metadata/constant.ts
similarity index 73%
copy from paimon-web-ui-new/src/views/metadata/index.module.scss
copy to paimon-web-ui-new/src/views/metadata/constant.ts
index 458f8d8..4bc583a 100644
--- a/paimon-web-ui-new/src/views/metadata/index.module.scss
+++ b/paimon-web-ui-new/src/views/metadata/constant.ts
@@ -15,21 +15,15 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.container {
- display: flex;
- width: 100%;
- height: 100%;
+import type { OptionsDTO, TableDTO } from "@/api/models/catalog"
- .content {
- display: flex;
- flex-direction: column;
-
- width: calc(100% - 60px);
-
- padding: 14px 20px;
-
- :global(.n-empty) {
- margin-top: 200px;
- }
+export const transformOption = (value: TableDTO) => {
+ const tableOptions: OptionsDTO = {}
+ value.options?.forEach((item) => {
+ tableOptions[item.key] = item.value
+ })
+ return {
+ ...value,
+ tableOptions
}
}
diff --git a/paimon-web-ui-new/src/views/metadata/index.module.scss b/paimon-web-ui-new/src/views/metadata/index.module.scss
index 458f8d8..91473a5 100644
--- a/paimon-web-ui-new/src/views/metadata/index.module.scss
+++ b/paimon-web-ui-new/src/views/metadata/index.module.scss
@@ -28,8 +28,10 @@ under the License. */
padding: 14px 20px;
- :global(.n-empty) {
- margin-top: 200px;
+ .empty {
+ :global(.n-empty) {
+ margin-top: 200px;
+ }
}
}
}
diff --git a/paimon-web-ui-new/src/views/metadata/index.tsx b/paimon-web-ui-new/src/views/metadata/index.tsx
index 13917f0..5b09581 100644
--- a/paimon-web-ui-new/src/views/metadata/index.tsx
+++ b/paimon-web-ui-new/src/views/metadata/index.tsx
@@ -18,7 +18,7 @@ under the License. */
import { useCatalogStore } from '@/store/catalog'
import styles from './index.module.scss'
-import MenuTree from "./components/menu-tree"
+import MenuTree from './components/menu-tree'
import Breadcrumb from './components/breadcrumb'
import Tabs from './components/metadata-tabs'
@@ -37,14 +37,18 @@ export default defineComponent({
<div class={styles.container}>
<MenuTree />
<div class={styles.content}>
- {
- this.currentTable ? <>
+ {this.currentTable ? (
+ <>
<Breadcrumb />
<Tabs />
- </> : <n-empty description='Select the table. Please'></n-empty>
- }
+ </>
+ ) : (
+ <div class={styles.empty}>
+ <n-empty description="Select the table. Please"></n-empty>
+ </div>
+ )}
</div>
</div>
- );
- },
-});
+ )
+ }
+})