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 3931c6e  [Feature] Support creating, updating and deleting role (#228)
3931c6e is described below

commit 3931c6e27ba47d28e296f046ad21a8f54b6a8978
Author: xiaomo <[email protected]>
AuthorDate: Thu May 16 14:11:57 2024 +0800

    [Feature] Support creating, updating and deleting role (#228)
---
 paimon-web-ui/license.node.js                      |   2 +-
 paimon-web-ui/src/api/models/role/index.ts         |  37 +++-
 paimon-web-ui/src/api/models/role/types/role.ts    |  21 ++-
 paimon-web-ui/src/layouts/content/index.tsx        |  13 +-
 paimon-web-ui/src/locales/en/modules/common.ts     |   3 +-
 paimon-web-ui/src/locales/en/modules/system.ts     |   7 +-
 paimon-web-ui/src/locales/zh/modules/common.ts     |  21 +--
 paimon-web-ui/src/locales/zh/modules/system.ts     |   7 +-
 .../common.ts => store/permission/index.ts}        |  26 +--
 .../metadata/components/columns-form/index.tsx     |   2 +-
 .../system/role/components/role-delete/index.tsx   |  54 ++++++
 .../system/role/components/role-detail/index.tsx   |  19 +-
 .../system/role/components/role-form/index.tsx     | 191 +++++++++++++++++++++
 paimon-web-ui/src/views/system/role/index.tsx      | 139 ++++++++++++++-
 .../src/views/system/role/use-constants.tsx        |  72 --------
 15 files changed, 494 insertions(+), 120 deletions(-)

diff --git a/paimon-web-ui/license.node.js b/paimon-web-ui/license.node.js
index f9e16e3..f468517 100644
--- a/paimon-web-ui/license.node.js
+++ b/paimon-web-ui/license.node.js
@@ -101,7 +101,7 @@ function write(data, fPath) {
 
   const fileName = path.basename(fPath);
   let license;
-  if (fileName.endsWith('.ts') || fileName.endsWith('.js') || 
fileName.endsWith('.css') || fileName.endsWith('.scss')) {
+  if (fileName.endsWith('.tsx') || fileName.endsWith('.ts') || 
fileName.endsWith('.js') || fileName.endsWith('.css') || 
fileName.endsWith('.scss')) {
     license = tsLicenses
   }
   if (fileName.endsWith('.vue') || fileName.endsWith('.html')) {
diff --git a/paimon-web-ui/src/api/models/role/index.ts 
b/paimon-web-ui/src/api/models/role/index.ts
index 758c186..3ea186c 100644
--- a/paimon-web-ui/src/api/models/role/index.ts
+++ b/paimon-web-ui/src/api/models/role/index.ts
@@ -17,16 +17,43 @@ under the License. */
 
 import httpRequest from '../../request'
 import type { ResponseOptions } from '@/api/types'
-import type { Role, RoleDetail, RoleParams } from './types/role'
+import type { Role, RoleDTO, RoleDetail, RoleMenu, RoleParams } from 
'./types/role'
+
+
+/**
+ * # create a role
+ */
+export const createRole = () => {
+  return httpRequest.createHooks!<unknown, RoleDTO>({
+    url: '/role',
+    method: 'post'
+  })
+}
+
+
+/**
+ * # update a role
+ */
+export const updateRole = () => {
+  return httpRequest.createHooks!<unknown, RoleDTO>({
+    url: '/role',
+    method: 'put'
+  })
+}
+
+/**
+ * # delete a role
+ */
+export const deleteRole = (roleId: number) => {
+  return httpRequest.delete!<unknown, RoleDTO>(`/role/${roleId}`)
+}
+
 
 /**
  * # permission tree
  */
 export const getPermissionTree = () => {
-  return httpRequest.createHooks!<ResponseOptions<Role[]>>({
-    url: '/menu/treeselect',
-    method: 'get',
-  })
+  return httpRequest.get!<string, 
ResponseOptions<RoleMenu[]>>(`/menu/treeselect`)
 }
 
 
diff --git a/paimon-web-ui/src/api/models/role/types/role.ts 
b/paimon-web-ui/src/api/models/role/types/role.ts
index 386e970..dad377b 100644
--- a/paimon-web-ui/src/api/models/role/types/role.ts
+++ b/paimon-web-ui/src/api/models/role/types/role.ts
@@ -23,13 +23,7 @@ export interface RoleDetail {
 export interface RoleMenu {
   id: number;
   label: string;
-  children: RoleMenuChild[];
-}
-
-export interface RoleMenuChild {
-  id: number;
-  label: string;
-  children: Omit<RoleMenuChild, 'children'>[];
+  children: RoleMenu[];
 }
 
 export interface Role {
@@ -41,7 +35,8 @@ export interface Role {
   sort: number;
   enabled: boolean;
   isDelete: boolean;
-  remark: null;
+  admin?: boolean;
+  remark?: string;
   flag: boolean;
   menuIds: null;
   permissions: null;
@@ -52,3 +47,13 @@ export interface RoleParams {
   currentPage: number,
   pageSize: number
 }
+
+export interface RoleDTO {
+  id?: number
+  roleName: string;
+  roleKey: string;
+  enabled: boolean;
+  remark?: string;
+  menuIds: number[];
+}
+
diff --git a/paimon-web-ui/src/layouts/content/index.tsx 
b/paimon-web-ui/src/layouts/content/index.tsx
index 08382a7..92bb212 100644
--- a/paimon-web-ui/src/layouts/content/index.tsx
+++ b/paimon-web-ui/src/layouts/content/index.tsx
@@ -20,10 +20,12 @@ import styles from './index.module.scss'
 import SideBar from './components/sidebar'
 import { useData } from './use-data'
 import { useConfigStore } from '@/store/config'
+import { usePermissionStore } from '@/store/permission'
 
 export default defineComponent({
   name: 'ContentPage',
   setup() {
+    const permissionStore = usePermissionStore()
     const configStore = useConfigStore()
     const { menuOptions, state } = useData()
     const getSideOption = (state: any) => {
@@ -31,10 +33,19 @@ export default defineComponent({
       state.sideMenuOptions = menuOptions.value.find((m: any) => m.key === 
activeNavKey)?.sideMenuOptions || []
       state.isShowSided = state.sideMenuOptions && state.sideMenuOptions.length
     }
+
+    onMounted(permissionStore.getPermissionList)
+
     getSideOption(state)
+
     watch(
       () => configStore.getCurrentNavActive,
-      () => { getSideOption(state)})
+      () => {
+        getSideOption(state)
+
+      }
+    )
+
     return {
       ...toRefs(state),
       menuOptions
diff --git a/paimon-web-ui/src/locales/en/modules/common.ts 
b/paimon-web-ui/src/locales/en/modules/common.ts
index d45a14f..c8c4c3a 100644
--- a/paimon-web-ui/src/locales/en/modules/common.ts
+++ b/paimon-web-ui/src/locales/en/modules/common.ts
@@ -25,5 +25,6 @@ export default {
     update_time:'Update time',
     search:'Search',
     yes:'Yes',
-    no:'No'
+    no:'No',
+    action: 'Action',
 }
diff --git a/paimon-web-ui/src/locales/en/modules/system.ts 
b/paimon-web-ui/src/locales/en/modules/system.ts
index 7d8ea8f..73eb422 100644
--- a/paimon-web-ui/src/locales/en/modules/system.ts
+++ b/paimon-web-ui/src/locales/en/modules/system.ts
@@ -24,9 +24,14 @@ const user = {
 }
 
 const role = {
+  create: 'Create Role',
+  update: ' Update Role',
   role_name: 'Role name',
   role_key: 'Role Key',
-  enabled: 'Enabled'
+  enabled: 'Enabled',
+  remark: 'Remark',
+  permission_setting: 'Permission Setting',
+  no_permission: 'No Permission',
 }
 
 const roleKey = {
diff --git a/paimon-web-ui/src/locales/zh/modules/common.ts 
b/paimon-web-ui/src/locales/zh/modules/common.ts
index b2a9b0e..223a6d8 100644
--- a/paimon-web-ui/src/locales/zh/modules/common.ts
+++ b/paimon-web-ui/src/locales/zh/modules/common.ts
@@ -16,14 +16,15 @@ specific language governing permissions and limitations
 under the License. */
 
 export default {
-    success:'成功',
-    add_success:'新增成功',
-    update_success:'更新成功',
-    delete_success:'删除成功',
-    create_user:'创建用户',
-    create_time:'创建时间',
-    update_time:'修改时间',
-    search:'搜索',
-    yes:'是',
-    no:'否'
+  success: '成功',
+  add_success: '新增成功',
+  update_success: '更新成功',
+  delete_success: '删除成功',
+  create_user: '创建用户',
+  create_time: '创建时间',
+  update_time: '修改时间',
+  search: '搜索',
+  yes: '是',
+  no: '否',
+  action: '操作',
 }
diff --git a/paimon-web-ui/src/locales/zh/modules/system.ts 
b/paimon-web-ui/src/locales/zh/modules/system.ts
index 6f91956..377ece8 100644
--- a/paimon-web-ui/src/locales/zh/modules/system.ts
+++ b/paimon-web-ui/src/locales/zh/modules/system.ts
@@ -24,9 +24,14 @@ const user = {
 }
 
 const role = {
+  create: '新增角色',
+  update: '更新角色',
   role_name: '角色名称',
   role_key: '角色编码',
-  enabled: '是否启用'
+  enabled: '是否启用',
+  remark: '备注',
+  permission_setting: '权限配置',
+  no_permission: '暂无权限'
 }
 
 const roleKey = {
diff --git a/paimon-web-ui/src/locales/en/modules/common.ts 
b/paimon-web-ui/src/store/permission/index.ts
similarity index 64%
copy from paimon-web-ui/src/locales/en/modules/common.ts
copy to paimon-web-ui/src/store/permission/index.ts
index d45a14f..2fdd2d0 100644
--- a/paimon-web-ui/src/locales/en/modules/common.ts
+++ b/paimon-web-ui/src/store/permission/index.ts
@@ -15,15 +15,17 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-export default {
-    success:'Success',
-    add_success:'Add success',
-    update_success:'Update success',
-    delete_success:'Delete success',
-    create_user:'Create user',
-    create_time:'Create time',
-    update_time:'Update time',
-    search:'Search',
-    yes:'Yes',
-    no:'No'
-}
+import { getPermissionTree } from "@/api/models/role"
+
+import type { RoleMenu } from "@/api/models/role/types/role"
+
+export const usePermissionStore = defineStore('permission', () => {
+  const permissionList = ref<RoleMenu[]>([])
+
+  const getPermissionList = async () => {
+    const res = await getPermissionTree()
+    permissionList.value = res.data || []
+  }
+
+  return { permissionList, getPermissionList }
+})
diff --git a/paimon-web-ui/src/views/metadata/components/columns-form/index.tsx 
b/paimon-web-ui/src/views/metadata/components/columns-form/index.tsx
index 88495e8..a74d86a 100644
--- a/paimon-web-ui/src/views/metadata/components/columns-form/index.tsx
+++ b/paimon-web-ui/src/views/metadata/components/columns-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 { Add, AddCircleOutline } from '@vicons/ionicons5'
+import { Add } from '@vicons/ionicons5'
 
 import { useCatalogStore } from '@/store/catalog'
 import { type ColumnDTO, createColumns } from '@/api/models/catalog'
diff --git 
a/paimon-web-ui/src/views/system/role/components/role-delete/index.tsx 
b/paimon-web-ui/src/views/system/role/components/role-delete/index.tsx
new file mode 100644
index 0000000..3bd7ec5
--- /dev/null
+++ b/paimon-web-ui/src/views/system/role/components/role-delete/index.tsx
@@ -0,0 +1,54 @@
+
+/* 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 { deleteRole } from "@/api/models/role"
+import { RemoveCircleOutline, Warning } from "@vicons/ionicons5"
+
+export default defineComponent({
+  props: {
+    roleId: {
+      type: Number
+    },
+    onDelete: Function
+  },
+  setup(props) {
+    const onDelete = async () => {
+      (props.roleId || props.roleId === 0) && await deleteRole(props.roleId)
+      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/role/components/role-detail/index.tsx 
b/paimon-web-ui/src/views/system/role/components/role-detail/index.tsx
index c927c03..5f827f8 100644
--- a/paimon-web-ui/src/views/system/role/components/role-detail/index.tsx
+++ b/paimon-web-ui/src/views/system/role/components/role-detail/index.tsx
@@ -32,15 +32,27 @@ export default defineComponent({
 
     const rolePermissionDetail = ref<RoleMenu[]>([])
     const loading = ref(false)
+
     onMounted(initDetail)
 
     async function initDetail() {
       loading.value = true
       const res = await getPermissionByRoleId(props.roleRecord.id)
-      rolePermissionDetail.value = res?.data?.menus
+      rolePermissionDetail.value = transformMenu(res?.data?.menus || [], 
res?.data?.checkedKeys || [])
       loading.value = false
     }
 
+    function transformMenu(menu: RoleMenu[], values: number[]) {
+      const result = menu.filter((item) => {
+        if (item.children?.length) {
+          item.children = transformMenu(item.children, values)
+        }
+        return values.includes(item.id)
+      })
+
+      return result
+    }
+
     return {
       loading,
       rolePermissionDetail,
@@ -51,7 +63,7 @@ export default defineComponent({
     return <n-spin show={this.loading}>
       <n-list hoverable clickable>
         {
-          this.rolePermissionDetail?.map((item) => (
+          this.rolePermissionDetail?.map((item, i) => (
             <n-list-item key={item.id}>
               <n-thing title={this.t(`system.roleKey.${item.label}`)} 
content-style="margin-top: 10px;">
                 {
@@ -71,6 +83,9 @@ export default defineComponent({
             </n-list-item>
           ))
         }
+        {
+          this.rolePermissionDetail?.length === 0 && <n-empty 
description={this.t('system.role.no_permission')} />
+        }
       </n-list>
     </n-spin>
   }
diff --git a/paimon-web-ui/src/views/system/role/components/role-form/index.tsx 
b/paimon-web-ui/src/views/system/role/components/role-form/index.tsx
index e69de29..3acf3b5 100644
--- a/paimon-web-ui/src/views/system/role/components/role-form/index.tsx
+++ b/paimon-web-ui/src/views/system/role/components/role-form/index.tsx
@@ -0,0 +1,191 @@
+
+/* 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 { RoleDTO } from "@/api/models/role/types/role";
+import { usePermissionStore } from "@/store/permission";
+import type { FormRules, TreeOption } from "naive-ui";
+
+const props = {
+  visible: {
+    type: Boolean as PropType<boolean>,
+    default: false
+  },
+  'onUpdate:visible': [Function, Boolean] as PropType<((value: boolean) => 
void) | undefined>,
+
+  modelLoading: {
+    type: Boolean as PropType<boolean>,
+  },
+  formType: {
+    type: String as PropType<'create' | 'update'>
+  },
+
+  formValue: {
+    type: Object as PropType<RoleDTO>,
+    default: () => ({
+      roleName: '',
+      roleKey: '',
+      enabled: true,
+      remark: '',
+      menuIds: []
+    })
+  },
+  'onUpdate:formValue': [Function, Object] as PropType<((value: RoleDTO) => 
void) | undefined>,
+  onConfirm: Function
+}
+
+export default defineComponent({
+  name: 'RoleForm',
+  props,
+  setup(props) {
+    const rules = {
+      roleName: {
+        required: true,
+        trigger: ['blur', 'input'],
+        message: 'role name required'
+      },
+      roleKey: {
+        required: true,
+        trigger: ['blur', 'input'],
+        message: 'role key required'
+      },
+      menuIds: {
+        required: true,
+        trigger: ['blur'],
+        validator: (_: FormRules, value: string) => {
+          return new Promise<void>((resolve, reject) => {
+            if (!value?.length) {
+              reject(Error('menu ids required'))
+            } else {
+              resolve()
+            }
+          })
+        }
+      }
+    }
+
+    const { t } = useLocaleHooks()
+    const permissionStore = usePermissionStore()
+    const { permissionList } = storeToRefs(permissionStore)
+
+    const formRef = ref()
+    
+    const handleCloseModal = () => {
+      props["onUpdate:visible"] && props["onUpdate:visible"](false)
+      resetState()
+    }
+
+    const renderLabel = ({ option }: { option: TreeOption }) => {
+      return t(`system.roleKey.${option.label}`)
+    }
+
+    const onUpdateMenuIds = (checkIds:Array<number>) => {
+      props.formValue.menuIds = checkIds
+    }
+
+    const handleConfirm = async () => {
+      await formRef.value.validate()
+      props && props.onConfirm && props.onConfirm()
+      handleCloseModal()
+      resetState()
+    }
+
+    const resetState = () => {
+      props["onUpdate:formValue"] && props["onUpdate:formValue"]({
+        roleName: '',
+        roleKey: '',
+        enabled: true,
+        remark: '',
+        menuIds: []
+      })
+    }
+
+    return {
+      ...toRefs(props),
+      formRef,
+      rules,
+      permissionTree: permissionList,
+      handleCloseModal,
+      renderLabel,
+      onUpdateMenuIds,
+      handleConfirm,
+      t,
+    }
+  },
+  render() {
+    return (
+        <n-modal v-model:show={this.visible} mask-closable={false}>
+          <n-card bordered={false} title={this.t(this.formType === 'create'? 
'system.role.create' : 'system.role.update')} style="width: 600px">
+            {{
+              default: () => (
+                  <n-form
+                    ref="formRef"
+                    label-placement="left"
+                    label-width="auto"
+                    label-align="left"
+                    rules={this.rules}
+                    model={this.formValue}
+                  >
+                    <n-form-item label={this.t('system.role.role_name')} 
path="roleName">
+                      <n-input v-model:value={this.formValue.roleName} />
+                    </n-form-item>
+                    <n-form-item label={this.t('system.role.role_key')} 
path="roleKey">
+                      <n-input v-model:value={this.formValue.roleKey} />
+                    </n-form-item>
+                    <n-form-item label={this.t('system.role.enabled')} 
path="enabled">
+                      <n-switch v-model:value={this.formValue.enabled} />
+                    </n-form-item>
+                    <n-form-item label={this.t('system.role.remark')} 
path="remark">
+                      <n-input 
+                        v-model:value={this.formValue.remark} 
+                        type="textarea"
+                        autosize={{
+                          minRows: 3,
+                          maxRows: 5
+                        }}
+                      />
+                    </n-form-item>
+                    <n-form-item 
label={this.t('system.role.permission_setting')} path="menuIds">
+                      <n-tree
+                        key-field='id'
+                        default-expand-all
+                        block-line
+                        cascade
+                        renderLabel={this.renderLabel}
+                        onUpdate:checkedKeys={this.onUpdateMenuIds}
+                        checkedKeys={this.formValue.menuIds}
+                        data={this.permissionTree}
+                        expand-on-click
+                        checkable
+                      />
+                    </n-form-item>
+                  </n-form>
+              ),
+              action: () => (
+                  <n-space justify="end">
+                    <n-button 
onClick={this.handleCloseModal}>{this.t('layout.cancel')}</n-button>
+                    <n-button loading={this.modelLoading} type="primary" 
onClick={this.handleConfirm}>
+                      {this.t('layout.confirm')}
+                    </n-button>
+                  </n-space>
+              )
+            }}
+          </n-card>
+        </n-modal>
+    );
+  }
+})
diff --git a/paimon-web-ui/src/views/system/role/index.tsx 
b/paimon-web-ui/src/views/system/role/index.tsx
index 0b5ae86..e9c85d2 100644
--- a/paimon-web-ui/src/views/system/role/index.tsx
+++ b/paimon-web-ui/src/views/system/role/index.tsx
@@ -15,39 +15,167 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-import type { Role } from '@/api/models/role/types/role';
+import type { Role, RoleDTO } from '@/api/models/role/types/role';
 import styles from './index.module.scss';
-import { useConstants } from './use-constants';
 import { useTable } from './use-table';
+import RoleForm from './components/role-form';
+import { createRole, getPermissionByRoleId, updateRole } from 
'@/api/models/role';
+import dayjs from 'dayjs';
+import RoleDetail from './components/role-detail';
+import { EditOutlined } from '@vicons/antd';
+import { RemoveCircleOutline, Warning } from '@vicons/ionicons5';
+import RoleDelete from './components/role-delete';
 
 export default defineComponent({
   name: 'RolePage',
   setup() {
+    const columns = [
+      {
+        type: 'expand',
+        resizable: true,
+        renderExpand: (row: Role) => {
+          return (
+            <RoleDetail roleRecord={row} />
+          )
+        }
+      },
+      {
+        title: () => t('system.role.role_name'),
+        key: 'roleName',
+        resizable: true
+      },
+      {
+        title: () => t('system.role.role_key'),
+        key: 'roleKey',
+        resizable: true
+      },
+      {
+        title: () => t('system.role.enabled'),
+        key: 'enabled',
+        resizable: true,
+        render: (row: Role) => {
+          return row.enabled ? t('common.yes') : t('common.no')
+        }
+      },
+      {
+        title: () => t('common.create_time'),
+        key: 'createTime',
+        resizable: true,
+        render: (row: Role) => {
+          return row?.createTime ? dayjs(row?.createTime).format('YYYY-MM-DD 
HH:mm') : '-'
+        }
+      },
+      {
+        title: () => t('common.update_time'),
+        key: 'updateTime',
+        resizable: true,
+        render: (row: Role) => {
+          return row?.updateTime ? dayjs(row?.updateTime).format('YYYY-MM-DD 
HH:mm') : '-'
+        }
+      },
+      {
+        title: () => t('common.action'),
+        key: 'actions',
+        resizable: true,
+        render: (row: Role) => {
+          if (!row?.admin) {
+            return (
+              <n-space>
+                <n-button onClick={() => handleUpdateModal(row)} strong 
secondary circle>
+                  {{
+                    icon: () => <n-icon component={EditOutlined} />
+                  }}
+                </n-button>
+                <RoleDelete roleId={row?.id} onDelete={getTableData} />
+              </n-space>
+            )
+          }
+          
+          return null
+        }
+      }
+    ]
+
     const { t } = useLocaleHooks()
     const { tableVariables, getTableData, roleList, loading } = useTable()
-    const { columns } = useConstants()
+    const [, createFetch, { loading: createLoading }] = createRole()
+    const [, updateFetch, { loading: updateLoading }] = updateRole()
+    const message = useMessage()
+
+    const formType = ref<'create' | 'update'>('create')
+    const formVisible = ref(false)
+    const formValue = ref<RoleDTO>({
+      roleName: '',
+      roleKey: '',
+      enabled: true,
+      remark: '',
+      menuIds: []
+    })
+
+    async function getDetail(role: Role) {
+      const res = await getPermissionByRoleId(role.id)
+      formValue.value = {
+        id: role.id,
+        roleName: role.roleName,
+        roleKey: role.roleKey,
+        enabled: role.enabled,
+        remark: role.remark,
+        menuIds: res?.data?.checkedKeys
+      }
+    }
 
     onMounted(getTableData)
 
     const rowKey = (rowData: Role) => rowData.id
 
+    const handleCreateModal = () => {
+      formType.value = 'create'
+      formVisible.value = true
+    }
+
+    const handleUpdateModal = async (role: Role) => {
+      formType.value = 'update'
+      await getDetail(role)
+      formVisible.value = true
+    }
+
+    const onConfirm = async () => {
+      const fn = formType.value === 'create' ? createFetch : updateFetch
+
+      await fn({
+        params: toRaw(formValue.value)
+      })
+
+      message.success(t(`Successfully`))
+
+      formVisible.value = false
+      getTableData()
+    }
+
+    const modelLoading = computed(() => createLoading.value || 
updateLoading.value)
+
     return {
       ...toRefs(tableVariables),
       t,
+      formVisible,
+      formValue,
       columns: toRef([...columns]),
       roleList,
       loading,
+      modelLoading,
       rowKey,
+      onConfirm,
+      handleCreateModal,
     }
   },
   render() {
     return (
       <n-space class={styles.container} vertical justify="center">
-        <n-card >
+        <n-card>
           <n-space vertical>
             <n-space justify="space-between">
               <n-space>
-                <n-button type="primary">{this.t('system.user.add')}</n-button>
+                <n-button onClick={this.handleCreateModal} 
type="primary">{this.t('system.user.add')}</n-button>
               </n-space>
               <n-space>
                 <></>
@@ -63,6 +191,7 @@ export default defineComponent({
             />
           </n-space>
         </n-card>
+        <RoleForm modelLoading={this.modelLoading} 
v-model:visible={this.formVisible} v-model:formValue={this.formValue} 
onConfirm={this.onConfirm} />
       </n-space>
     )
   }
diff --git a/paimon-web-ui/src/views/system/role/use-constants.tsx 
b/paimon-web-ui/src/views/system/role/use-constants.tsx
deleted file mode 100644
index ce2ae81..0000000
--- a/paimon-web-ui/src/views/system/role/use-constants.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-/* 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 { Role } from "@/api/models/role/types/role"
-import dayjs from "dayjs"
-
-import RoleDetail from "./components/role-detail"
-
-export const useConstants = () => {
-  const { t } = useLocaleHooks()
-  const columns = [
-    {
-      type: 'expand',
-      resizable: true,
-      renderExpand: (row: Role) => {
-        return (
-          <RoleDetail roleRecord={row} />
-        )
-      }
-    },
-    {
-      title: () => t('system.role.role_name'),
-      key: 'roleName',
-      resizable: true
-    },
-    {
-      title: () => t('system.role.role_key'),
-      key: 'roleKey',
-      resizable: true
-    },
-    {
-      title: () => t('system.role.enabled'),
-      key: 'enabled',
-      resizable: true,
-      render: (row: Role) => {
-        return row.enabled ? t('common.yes') : t('common.no')
-      }
-    },
-    {
-      title: () => t('common.create_time'),
-      key: 'createTime',
-      resizable: true,
-      render: (row: Role) => {
-        return row?.createTime ? dayjs(row?.createTime).format('YYYY-MM-DD 
HH:mm') : '-'
-      }
-    },
-    {
-      title: () => t('common.update_time'),
-      key: 'updateTime',
-      resizable: true,
-      render: (row: Role) => {
-        return row?.updateTime ? dayjs(row?.updateTime).format('YYYY-MM-DD 
HH:mm') : '-'
-      }
-    },
-  ]
-
-  return { columns }
-}

Reply via email to