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/paimon-webui.git


The following commit(s) were added to refs/heads/main by this push:
     new 722a5a20 [Feature] Support cluster management (#258)
722a5a20 is described below

commit 722a5a208b226f54c4cae7c2d2e7d6f7b1274231
Author: xiaomo <[email protected]>
AuthorDate: Mon May 27 15:59:02 2024 +0800

    [Feature] Support cluster management (#258)
---
 paimon-web-ui/__unconfig_vite.config.ts            | 89 ---------------------
 paimon-web-ui/src/api/models/cluster/index.ts      | 57 ++++++++++++++
 .../models/cluster/types.ts}                       | 34 +++++---
 paimon-web-ui/src/api/request.ts                   |  9 ++-
 paimon-web-ui/src/layouts/content/use-data.ts      |  6 +-
 paimon-web-ui/src/locales/en/modules/layout.ts     |  1 +
 paimon-web-ui/src/locales/en/modules/system.ts     | 12 ++-
 paimon-web-ui/src/locales/zh/modules/layout.ts     |  1 +
 paimon-web-ui/src/locales/zh/modules/system.ts     | 12 ++-
 paimon-web-ui/src/router/modules/system.ts         |  6 ++
 .../cluster/components/cluster-delete/index.tsx    | 55 +++++++++++++
 .../components/cluster-form}/index.tsx             | 92 +++++++++-------------
 .../system/{user => cluster}/index.module.scss     | 10 +--
 .../src/views/system/{user => cluster}/index.tsx   | 92 ++++++++++------------
 .../system/user/components/user-form/index.tsx     | 12 +--
 .../src/views/system/user/index.module.scss        | 10 +--
 paimon-web-ui/src/views/system/user/index.tsx      |  6 +-
 17 files changed, 271 insertions(+), 233 deletions(-)

diff --git a/paimon-web-ui/__unconfig_vite.config.ts 
b/paimon-web-ui/__unconfig_vite.config.ts
deleted file mode 100644
index d6c159e2..00000000
--- a/paimon-web-ui/__unconfig_vite.config.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-
-let __unconfig_data;
-let __unconfig_stub = function (data = {}) { __unconfig_data = data };
-__unconfig_stub.default = (data = {}) => { __unconfig_data = data };
-/* 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 { URL, fileURLToPath } from 'node:url'
-
-import { defineConfig } from 'vite'
-import vue from '@vitejs/plugin-vue'
-import vueJsx from '@vitejs/plugin-vue-jsx'
-import AutoImport from 'unplugin-auto-import/vite'
-import Components from 'unplugin-vue-components/vite'
-import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
-import autoprefixer from 'autoprefixer'
-
-// https://vitejs.dev/config/
-const __unconfig_default =  defineConfig({
-  server: {
-    proxy: {
-      '/mock': {
-        target: 'http://127.0.0.1:10088',
-        changeOrigin: true,
-      },
-      '/api': {
-        target: 'http://127.0.0.1:10088',
-        changeOrigin: true,
-      },
-    },
-  },
-  plugins: [
-    vue(),
-    vueJsx(),
-    AutoImport({
-      imports: [
-        'vue',
-        'vue-router',
-        'pinia',
-        {
-          'naive-ui': [
-            'useDialog',
-            'useMessage',
-            'useNotification',
-            'useLoadingBar',
-          ],
-        },
-      ],
-      dts: './auto-imports.d.ts',
-      dirs: [
-        './src/composables',
-      ],
-      eslintrc: {
-        enabled: false,
-      },
-    }),
-    Components({
-      resolvers: [NaiveUiResolver()],
-    }),
-  ],
-  css: {
-    postcss: {
-      plugins: [
-        autoprefixer(),
-      ],
-    },
-  },
-  resolve: {
-    alias: {
-      '@': fileURLToPath(new URL('./src', import.meta.url)),
-    },
-  },
-})
-
-if (typeof __unconfig_default === "function") 
__unconfig_default(...[{"command":"serve","mode":"development"}]);export 
default __unconfig_data;
\ No newline at end of file
diff --git a/paimon-web-ui/src/api/models/cluster/index.ts 
b/paimon-web-ui/src/api/models/cluster/index.ts
new file mode 100644
index 00000000..92f88092
--- /dev/null
+++ b/paimon-web-ui/src/api/models/cluster/index.ts
@@ -0,0 +1,57 @@
+/* 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 httpRequest from '../../request'
+import type { Cluster, ClusterDTO, ClusterNameParams } from './types'
+import type { ResponseOptions } from '@/api/types'
+
+/**
+ * # List Cluster
+ */
+export function getClusterList() {
+  return httpRequest.createHooks!<ResponseOptions<Cluster[]>, 
ClusterNameParams>({
+    url: '/cluster/list',
+    method: 'get',
+  })
+}
+
+/**
+ * # Create Cluster
+ */
+export function createCluster() {
+  return httpRequest.createHooks!<unknown, ClusterDTO>({
+    url: '/cluster',
+    method: 'post',
+  })
+}
+
+/**
+ * # Update user
+ */
+export function updateCluster() {
+  return httpRequest.createHooks!<unknown, ClusterDTO>({
+    url: '/cluster',
+    method: 'put',
+  })
+}
+
+/**
+ * # delete a Cluster
+ */
+export function deleteCluster(userId: number) {
+  return httpRequest.delete!<unknown, ClusterDTO>(`/cluster/${userId}`)
+}
diff --git a/paimon-web-ui/src/views/system/user/index.module.scss 
b/paimon-web-ui/src/api/models/cluster/types.ts
similarity index 63%
copy from paimon-web-ui/src/views/system/user/index.module.scss
copy to paimon-web-ui/src/api/models/cluster/types.ts
index fe437d97..0a1dd559 100644
--- a/paimon-web-ui/src/views/system/user/index.module.scss
+++ b/paimon-web-ui/src/api/models/cluster/types.ts
@@ -15,14 +15,30 @@ 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%;
+export interface Cluster {
+  id: number
+  createTime?: string
+  updateTime?: string
+  clusterName: string
+  host: string
+  port: number
+  type: string
+  enabled: boolean
+}
+
+export interface ClusterNameParams {
+  clusterName?: string
+  pageNum: number
+  pageSize: number
+}
 
-  .content {
-    display: flex;
-    width: calc(100% - 60px);
-    height: 100%;
-  }
+export interface ClusterDTO {
+  id?: number
+  clusterName: string
+  host: string
+  port: number
+  type: string
+  enabled: boolean
+  createTime?: string
+  updateTime?: string
 }
diff --git a/paimon-web-ui/src/api/request.ts b/paimon-web-ui/src/api/request.ts
index 4bf2152b..d75f30b0 100644
--- a/paimon-web-ui/src/api/request.ts
+++ b/paimon-web-ui/src/api/request.ts
@@ -20,6 +20,7 @@ import { createUseAxle } from '@varlet/axle/use'
 
 import type { FetchOptions, ResponseOptions } from './types'
 import discreteApi from './message'
+import router from '@/router'
 
 const axle: AxleInstance & { createHooks?: typeof createHooks } = createAxle({
   baseURL: import.meta.env.MODE === 'mock' ? '/mock/api' : '/api',
@@ -41,7 +42,6 @@ axle.axios.interceptors.request.use(
 axle.axios.interceptors.response.use(
   (response) => {
     const { code, msg } = response.data
-
     if (code !== 200 && msg) {
       // do something there
       discreteApi.notification.error(
@@ -58,10 +58,15 @@ axle.axios.interceptors.response.use(
     return response.data
   },
   (error) => {
+    const { data } = error.response
+
+    if (data.code === 401)
+      router.replace('/login')
+
     discreteApi.notification.error(
       {
         content: 'Error',
-        meta: error,
+        meta: data.msg,
         duration: 2500,
         keepAliveOnHover: true,
       },
diff --git a/paimon-web-ui/src/layouts/content/use-data.ts 
b/paimon-web-ui/src/layouts/content/use-data.ts
index 24b622c2..7579d1d4 100644
--- a/paimon-web-ui/src/layouts/content/use-data.ts
+++ b/paimon-web-ui/src/layouts/content/use-data.ts
@@ -16,7 +16,7 @@ specific language governing permissions and limitations
 under the License. */
 
 import { RouterLink } from 'vue-router'
-import { FileSyncOutlined, UserOutlined, UserSwitchOutlined } from 
'@vicons/antd'
+import { ClusterOutlined, FileSyncOutlined, UserOutlined, UserSwitchOutlined } 
from '@vicons/antd'
 import { Catalog, Code } from '@vicons/carbon'
 import { NIcon } from 'naive-ui'
 import { SettingsOutline } from '@vicons/ionicons5'
@@ -67,6 +67,10 @@ export function useData() {
         label: () => renderLabel(t('layout.role'), 'system'),
         key: '/system/role',
         icon: renderIcon(UserSwitchOutlined),
+      }, {
+        label: () => renderLabel(t('layout.cluster'), 'system'),
+        key: '/system/cluster',
+        icon: renderIcon(ClusterOutlined),
       }],
     },
   ]))
diff --git a/paimon-web-ui/src/locales/en/modules/layout.ts 
b/paimon-web-ui/src/locales/en/modules/layout.ts
index e99ded8c..bf454ad1 100644
--- a/paimon-web-ui/src/locales/en/modules/layout.ts
+++ b/paimon-web-ui/src/locales/en/modules/layout.ts
@@ -26,4 +26,5 @@ export default {
   confirm: 'Confirm',
   user: 'User',
   role: 'Role',
+  cluster: 'Cluster',
 }
diff --git a/paimon-web-ui/src/locales/en/modules/system.ts 
b/paimon-web-ui/src/locales/en/modules/system.ts
index 7fe5b7f0..dc861cd9 100644
--- a/paimon-web-ui/src/locales/en/modules/system.ts
+++ b/paimon-web-ui/src/locales/en/modules/system.ts
@@ -61,4 +61,14 @@ const roleKey = {
   role_delete: 'Role Delete',
 }
 
-export default { user, role, roleKey }
+const cluster = {
+  cluster_name: 'Cluster Name',
+  cluster_type: 'Cluster Type',
+  cluster_host: 'Cluster Host',
+  cluster_port: 'Cluster Port',
+  enabled: 'Enabled',
+  create: 'Create Cluster',
+  update: 'Update Cluster',
+}
+
+export default { user, role, roleKey, cluster }
diff --git a/paimon-web-ui/src/locales/zh/modules/layout.ts 
b/paimon-web-ui/src/locales/zh/modules/layout.ts
index a3d0dbb5..4e222f4a 100644
--- a/paimon-web-ui/src/locales/zh/modules/layout.ts
+++ b/paimon-web-ui/src/locales/zh/modules/layout.ts
@@ -26,4 +26,5 @@ export default {
   confirm: '确认',
   user: '用户管理',
   role: '角色管理',
+  cluster: '集群管理',
 }
diff --git a/paimon-web-ui/src/locales/zh/modules/system.ts 
b/paimon-web-ui/src/locales/zh/modules/system.ts
index 1530974d..030716ce 100644
--- a/paimon-web-ui/src/locales/zh/modules/system.ts
+++ b/paimon-web-ui/src/locales/zh/modules/system.ts
@@ -61,4 +61,14 @@ const roleKey = {
   role_delete: '角色删除',
 }
 
-export default { user, role, roleKey }
+const cluster = {
+  cluster_name: '集群名称',
+  cluster_type: '集群类型',
+  cluster_host: '集群地址',
+  cluster_port: '集群端口',
+  enabled: '是否启用',
+  create: '新增集群',
+  update: '更新集群',
+}
+
+export default { user, role, roleKey, cluster }
diff --git a/paimon-web-ui/src/router/modules/system.ts 
b/paimon-web-ui/src/router/modules/system.ts
index c121309a..db2b9aa9 100644
--- a/paimon-web-ui/src/router/modules/system.ts
+++ b/paimon-web-ui/src/router/modules/system.ts
@@ -32,5 +32,11 @@ export default {
       meta: { title: 'role' },
       component: () => import('@/views/system/role'),
     },
+    {
+      path: '/system/cluster',
+      name: 'system-cluster',
+      meta: { title: 'cluster' },
+      component: () => import('@/views/system/cluster'),
+    },
   ],
 }
diff --git 
a/paimon-web-ui/src/views/system/cluster/components/cluster-delete/index.tsx 
b/paimon-web-ui/src/views/system/cluster/components/cluster-delete/index.tsx
new file mode 100644
index 00000000..4a7e674a
--- /dev/null
+++ b/paimon-web-ui/src/views/system/cluster/components/cluster-delete/index.tsx
@@ -0,0 +1,55 @@
+/* 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 { RemoveCircleOutline, Warning } from '@vicons/ionicons5'
+import { deleteCluster } from '@/api/models/cluster'
+
+export default defineComponent({
+  props: {
+    clusterId: {
+      type: Number,
+    },
+    onDelete: Function,
+  },
+  setup(props) {
+    const onDelete = async () => {
+      (props.clusterId || props.clusterId === 0) && await 
deleteCluster(props.clusterId)
+      props.onDelete && props.onDelete()
+    }
+
+    return {
+      onDelete,
+    }
+  },
+  render() {
+    return (
+      <n-popconfirm onPositiveClick={this.onDelete}>
+        {{
+          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>
+    )
+  },
+})
diff --git a/paimon-web-ui/src/views/system/user/components/user-form/index.tsx 
b/paimon-web-ui/src/views/system/cluster/components/cluster-form/index.tsx
similarity index 63%
copy from paimon-web-ui/src/views/system/user/components/user-form/index.tsx
copy to paimon-web-ui/src/views/system/cluster/components/cluster-form/index.tsx
index 5bd86aef..37c3c6f6 100644
--- a/paimon-web-ui/src/views/system/user/components/user-form/index.tsx
+++ b/paimon-web-ui/src/views/system/cluster/components/cluster-form/index.tsx
@@ -15,9 +15,9 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-import type { FormItemRule, TreeOption } from 'naive-ui'
+import type { FormItemRule } from 'naive-ui'
 
-import type { UserDTO } from '@/api/models/user/types'
+import type { ClusterDTO } from '@/api/models/cluster/types'
 import { listRoles } from '@/api/models/role'
 
 const props = {
@@ -35,16 +35,16 @@ const props = {
   },
 
   'formValue': {
-    type: Object as PropType<UserDTO>,
+    type: Object as PropType<ClusterDTO>,
     default: () => ({
-      roleName: '',
-      roleKey: '',
+      clusterName: '',
+      host: '',
+      port: 0,
       enabled: true,
-      remark: '',
-      menuIds: [],
+      type: '',
     }),
   },
-  'onUpdate:formValue': [Function, Object] as PropType<((value: UserDTO) => 
void) | undefined>,
+  'onUpdate:formValue': [Function, Object] as PropType<((value: ClusterDTO) => 
void) | undefined>,
   'onConfirm': Function,
 }
 
@@ -52,45 +52,39 @@ export default defineComponent({
   name: 'UserForm',
   props,
   setup(props) {
+    const typeOptions = [
+      { label: 'Flink', value: 'Flink' },
+      { label: 'Spark', value: 'Spark' },
+    ]
+
     const rules = {
-      username: {
+      clusterName: {
         required: true,
         trigger: ['blur', 'input'],
-        message: 'username required',
+        message: 'clusterName required',
       },
-      password: {
+      host: {
         required: true,
         trigger: ['blur', 'input'],
-        message: 'password required',
+        message: 'host required',
       },
-      roleIds: {
+      port: {
         required: true,
         type: 'number',
         trigger: ['blur', 'change'],
-        message: 'roleIds required',
+        message: 'port required',
       },
-      mobile: {
+      type: {
         required: true,
-        trigger: ['input'],
-        validator: (rule: FormItemRule, value: string) => {
-          return /^1+[3,8]+\d{9}$/.test(value)
-        },
+        trigger: ['blur', 'input'],
+        message: 'type required',
       },
     }
 
     const { t } = useLocaleHooks()
-    const [roleList, useRoleList] = listRoles()
 
     const formRef = ref()
 
-    watch(
-      () => props.visible,
-      (visible) => {
-        if (visible)
-          useRoleList()
-      },
-    )
-
     const handleCloseModal = () => {
       props['onUpdate:visible'] && props['onUpdate:visible'](false)
       resetState()
@@ -105,21 +99,19 @@ export default defineComponent({
 
     function resetState() {
       props['onUpdate:formValue'] && props['onUpdate:formValue']({
-        username: '',
-        nickname: '',
-        password: '',
+        clusterName: '',
+        host: '',
+        port: 0,
         enabled: true,
-        mobile: '',
-        email: '',
-        roleIds: undefined,
+        type: '',
       })
     }
 
     return {
       ...toRefs(props),
+      typeOptions,
       formRef,
       rules,
-      roleList,
       handleCloseModal,
       handleConfirm,
       t,
@@ -139,34 +131,22 @@ export default defineComponent({
                 rules={this.rules}
                 model={this.formValue}
               >
-                <n-form-item label={this.t('system.user.username')} 
path="username">
-                  <n-input v-model:value={this.formValue.username} />
-                </n-form-item>
-                <n-form-item label={this.t('system.user.nickname')} 
path="nickname">
-                  <n-input v-model:value={this.formValue.nickname} />
+                <n-form-item label={this.t('system.cluster.cluster_name')} 
path="clusterName">
+                  <n-input v-model:value={this.formValue.clusterName} />
                 </n-form-item>
-                {
-                  this.formType === 'create' && (
-                    <n-form-item label={this.t('system.user.password')} 
path="password">
-                      <n-input type="password" show-password-on="click" 
v-model:value={this.formValue.password} />
-                    </n-form-item>
-                  )
-                }
-                <n-form-item label={this.t('system.user.mobile')} 
path="mobile">
-                  <n-input v-model:value={this.formValue.mobile} />
+                <n-form-item label={this.t('system.cluster.cluster_host')} 
path="host">
+                  <n-input v-model:value={this.formValue.host} />
                 </n-form-item>
-                <n-form-item label={this.t('system.user.email')} path="email">
-                  <n-input v-model:value={this.formValue.email} />
+                <n-form-item label={this.t('system.cluster.cluster_port')} 
path="port">
+                  <n-input-number min={0} showButton={false} style="width: 
100%" v-model:value={this.formValue.port} />
                 </n-form-item>
                 <n-form-item label={this.t('system.user.enabled')} 
path="enabled">
                   <n-switch v-model:value={this.formValue.enabled} />
                 </n-form-item>
-                <n-form-item label={this.t('system.user.roleIds')} 
path="roleIds">
+                <n-form-item label={this.t('system.cluster.cluster_type')} 
path="type">
                   <n-select
-                    v-model:value={this.formValue.roleIds}
-                    options={this.roleList || []}
-                    value-field="id"
-                    label-field="roleName"
+                    v-model:value={this.formValue.type}
+                    options={this.typeOptions}
                   />
                 </n-form-item>
               </n-form>
diff --git a/paimon-web-ui/src/views/system/user/index.module.scss 
b/paimon-web-ui/src/views/system/cluster/index.module.scss
similarity index 87%
copy from paimon-web-ui/src/views/system/user/index.module.scss
copy to paimon-web-ui/src/views/system/cluster/index.module.scss
index fe437d97..41826413 100644
--- a/paimon-web-ui/src/views/system/user/index.module.scss
+++ b/paimon-web-ui/src/views/system/cluster/index.module.scss
@@ -17,12 +17,6 @@ under the License. */
 
 .container {
   display: flex;
-  width: 100%;
-  height: 100%;
-
-  .content {
-    display: flex;
-    width: calc(100% - 60px);
-    height: 100%;
-  }
+  width: calc(100% - 60px);
+  padding: 30px;
 }
diff --git a/paimon-web-ui/src/views/system/user/index.tsx 
b/paimon-web-ui/src/views/system/cluster/index.tsx
similarity index 66%
copy from paimon-web-ui/src/views/system/user/index.tsx
copy to paimon-web-ui/src/views/system/cluster/index.tsx
index 29ce3526..d07a0862 100644
--- a/paimon-web-ui/src/views/system/user/index.tsx
+++ b/paimon-web-ui/src/views/system/cluster/index.tsx
@@ -19,53 +19,57 @@ import type { TableColumns } from 
'naive-ui/es/data-table/src/interface'
 import dayjs from 'dayjs'
 import { EditOutlined } from '@vicons/antd'
 
-import UserForm from './components/user-form'
-import UserDelete from './components/user-delete'
+import ClusterForm from './components/cluster-form'
+import ClusterDelete from './components/cluster-delete'
 
 import styles from './index.module.scss'
 
-import { createUser, getUserList, updateUser } from '@/api/models/user'
+import { createCluster, getClusterList, updateCluster } from 
'@/api/models/cluster'
 
-import type { User, UserDTO } from '@/api/models/user/types'
+import type { ClusterDTO } from '@/api/models/cluster/types'
 
 export default defineComponent({
-  name: 'UserPage',
+  name: 'ClusterPage',
   setup() {
     const { t } = useLocaleHooks()
     const rowKey = (rowData: any) => rowData.id
     const message = useMessage()
 
-    const columns: TableColumns<UserDTO> = [
+    const columns: TableColumns<ClusterDTO> = [
       {
-        title: () => t('system.user.username'),
-        key: 'username',
+        title: () => t('system.cluster.cluster_name'),
+        key: 'clusterName',
       },
       {
-        title: () => t('system.user.nickname'),
-        key: 'nickname',
+        title: () => t('system.cluster.cluster_host'),
+        key: 'host',
       },
       {
-        title: () => t('system.user.mobile'),
-        key: 'mobile',
+        title: () => t('system.cluster.cluster_port'),
+        key: 'port',
       },
       {
-        title: () => t('system.user.enabled'),
+        title: () => t('system.cluster.cluster_type'),
+        key: 'type',
+      },
+      {
+        title: () => t('system.cluster.enabled'),
         key: 'enabled',
-        render: (row: UserDTO) => {
+        render: (row: ClusterDTO) => {
           return row.enabled ? t('common.yes') : t('common.no')
         },
       },
       {
         title: () => t('common.create_time'),
         key: 'createTime',
-        render: (row: UserDTO) => {
+        render: (row: ClusterDTO) => {
           return row?.createTime ? dayjs(row?.createTime).format('YYYY-MM-DD 
HH:mm') : '-'
         },
       },
       {
         title: () => t('common.update_time'),
         key: 'updateTime',
-        render: (row: UserDTO) => {
+        render: (row: ClusterDTO) => {
           return row?.updateTime ? dayjs(row?.updateTime).format('YYYY-MM-DD 
HH:mm') : '-'
         },
       },
@@ -73,7 +77,7 @@ export default defineComponent({
         title: () => t('common.action'),
         key: 'actions',
         resizable: true,
-        render: (row: UserDTO) => {
+        render: (row: ClusterDTO) => {
           return (
             <n-space>
               <n-button onClick={() => handleUpdateModal(row)} strong 
secondary circle>
@@ -81,28 +85,26 @@ export default defineComponent({
                   icon: () => <n-icon component={EditOutlined} />,
                 }}
               </n-button>
-              <UserDelete userId={row?.id} onDelete={getTableData} />
+              <ClusterDelete clusterId={row?.id} onDelete={getTableData} />
             </n-space>
           )
         },
       },
     ]
 
-    const [userList, useAccountList, { loading }] = getUserList()
-    const [, createFetch, { loading: createLoading }] = createUser()
-    const [, updateFetch, { loading: updateLoading }] = updateUser()
+    const [clusterList, useClusterList, { loading }] = getClusterList()
+    const [, createFetch, { loading: createLoading }] = createCluster()
+    const [, updateFetch, { loading: updateLoading }] = updateCluster()
 
     const formType = ref<'create' | 'update'>('create')
     const formVisible = ref(false)
 
-    const formValue = ref<UserDTO>({
-      username: '',
-      password: '',
-      nickname: '',
+    const formValue = ref<ClusterDTO>({
+      clusterName: '',
+      host: '',
+      port: 0,
+      type: '',
       enabled: true,
-      mobile: '',
-      email: '',
-      roleIds: [],
     })
 
     onMounted(getTableData)
@@ -112,20 +114,19 @@ export default defineComponent({
       formVisible.value = true
     }
 
-    async function handleUpdateModal(user: UserDTO) {
+    async function handleUpdateModal(cluster: ClusterDTO) {
       formType.value = 'update'
 
-      delete user.createTime
-      delete user.updateTime
-      delete user.roles
+      delete cluster.createTime
+      delete cluster.updateTime
 
-      formValue.value = { ...user }
+      formValue.value = { ...cluster }
       formVisible.value = true
     }
 
     const tableVariables = reactive({
       searchForm: {
-        username: '',
+        clusterName: '',
       },
       pagination: {
         showQuickJumper: true,
@@ -142,11 +143,10 @@ export default defineComponent({
 
     function getTableData() {
       const params = {
-        username: tableVariables.searchForm.username,
         pageNum: tableVariables.pagination.page,
         pageSize: tableVariables.pagination.pageSize,
       }
-      useAccountList({ params })
+      useClusterList({ params })
     }
 
     const modelLoading = computed(() => createLoading.value || 
updateLoading.value)
@@ -156,9 +156,6 @@ export default defineComponent({
 
       const params = { ...toRaw(formValue.value) }
 
-      if (!Array.isArray(params.roleIds))
-        params.roleIds = [params.roleIds || '']
-
       await fn({
         params,
       })
@@ -175,7 +172,7 @@ export default defineComponent({
       modelLoading,
       columns,
       loading,
-      userList,
+      clusterList,
       ...toRefs(tableVariables),
 
       formType,
@@ -187,20 +184,15 @@ export default defineComponent({
   },
   render() {
     return (
-      <div class={styles.container}>
+      <n-space class={styles.container} vertical justify="center">
         <n-card>
           <n-space vertical>
             <n-space justify="space-between">
-              <n-space>
-                <n-button onClick={this.handleCreateModal} 
type="primary">{this.t('system.user.add')}</n-button>
-              </n-space>
-              <n-space>
-                <></>
-              </n-space>
+              <n-button onClick={this.handleCreateModal} 
type="primary">{this.t('system.user.add')}</n-button>
             </n-space>
             <n-data-table
               columns={this.columns}
-              data={this.userList || []}
+              data={this.clusterList || []}
               pagination={this.pagination}
               loading={this.loading}
               remote
@@ -208,8 +200,8 @@ export default defineComponent({
             />
           </n-space>
         </n-card>
-        <UserForm modelLoading={this.modelLoading} formType={this.formType} 
v-model:visible={this.formVisible} v-model:formValue={this.formValue} 
onConfirm={this.onConfirm} />
-      </div>
+        <ClusterForm modelLoading={this.modelLoading} formType={this.formType} 
v-model:visible={this.formVisible} v-model:formValue={this.formValue} 
onConfirm={this.onConfirm} />
+      </n-space>
     )
   },
 })
diff --git a/paimon-web-ui/src/views/system/user/components/user-form/index.tsx 
b/paimon-web-ui/src/views/system/user/components/user-form/index.tsx
index 5bd86aef..70f9ab67 100644
--- a/paimon-web-ui/src/views/system/user/components/user-form/index.tsx
+++ b/paimon-web-ui/src/views/system/user/components/user-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 { FormItemRule, TreeOption } from 'naive-ui'
+import type { FormItemRule } from 'naive-ui'
 
 import type { UserDTO } from '@/api/models/user/types'
 import { listRoles } from '@/api/models/role'
@@ -37,11 +37,13 @@ const props = {
   'formValue': {
     type: Object as PropType<UserDTO>,
     default: () => ({
-      roleName: '',
-      roleKey: '',
+      username: '',
+      nickname: '',
+      password: '',
       enabled: true,
-      remark: '',
-      menuIds: [],
+      mobile: '',
+      email: '',
+      roleIds: undefined,
     }),
   },
   'onUpdate:formValue': [Function, Object] as PropType<((value: UserDTO) => 
void) | undefined>,
diff --git a/paimon-web-ui/src/views/system/user/index.module.scss 
b/paimon-web-ui/src/views/system/user/index.module.scss
index fe437d97..41826413 100644
--- a/paimon-web-ui/src/views/system/user/index.module.scss
+++ b/paimon-web-ui/src/views/system/user/index.module.scss
@@ -17,12 +17,6 @@ under the License. */
 
 .container {
   display: flex;
-  width: 100%;
-  height: 100%;
-
-  .content {
-    display: flex;
-    width: calc(100% - 60px);
-    height: 100%;
-  }
+  width: calc(100% - 60px);
+  padding: 30px;
 }
diff --git a/paimon-web-ui/src/views/system/user/index.tsx 
b/paimon-web-ui/src/views/system/user/index.tsx
index 29ce3526..67a90281 100644
--- a/paimon-web-ui/src/views/system/user/index.tsx
+++ b/paimon-web-ui/src/views/system/user/index.tsx
@@ -26,7 +26,7 @@ import styles from './index.module.scss'
 
 import { createUser, getUserList, updateUser } from '@/api/models/user'
 
-import type { User, UserDTO } from '@/api/models/user/types'
+import type { UserDTO } from '@/api/models/user/types'
 
 export default defineComponent({
   name: 'UserPage',
@@ -187,7 +187,7 @@ export default defineComponent({
   },
   render() {
     return (
-      <div class={styles.container}>
+      <n-space class={styles.container} vertical justify="center">
         <n-card>
           <n-space vertical>
             <n-space justify="space-between">
@@ -209,7 +209,7 @@ export default defineComponent({
           </n-space>
         </n-card>
         <UserForm modelLoading={this.modelLoading} formType={this.formType} 
v-model:visible={this.formVisible} v-model:formValue={this.formValue} 
onConfirm={this.onConfirm} />
-      </div>
+      </n-space>
     )
   },
 })

Reply via email to