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 44edfeb [Feature] Introduce user management (#244)
44edfeb is described below
commit 44edfeb499f8314d53eb6e04478bc078e80c132c
Author: xiaomo <[email protected]>
AuthorDate: Sun May 26 11:32:57 2024 +0800
[Feature] Introduce user management (#244)
---
.../{vite.config.ts => __unconfig_vite.config.ts} | 41 +++--
paimon-web-ui/mock/modules/index.js | 4 +-
paimon-web-ui/mock/modules/system.js | 152 ++++++++++++++++-
paimon-web-ui/src/api/models/role/index.ts | 2 +-
.../api/models/role/{types/role.ts => types.ts} | 0
.../src/api/models/{role => user}/index.ts | 54 +++---
.../models/{role/types/role.ts => user/types.ts} | 56 +++---
paimon-web-ui/src/locales/en/modules/system.ts | 7 +
paimon-web-ui/src/locales/zh/modules/system.ts | 7 +
paimon-web-ui/src/store/permission/index.ts | 2 +-
.../system/role/components/role-form/index.tsx | 2 +-
paimon-web-ui/src/views/system/role/index.tsx | 2 +-
paimon-web-ui/src/views/system/role/use-table.ts | 2 +-
.../system/user/components/user-delete/index.tsx | 55 ++++++
.../components/user-form}/index.tsx | 126 +++++++-------
paimon-web-ui/src/views/system/user/index.tsx | 189 ++++++++++++++++++++-
paimon-web-ui/vite.config.ts | 33 ++--
17 files changed, 562 insertions(+), 172 deletions(-)
diff --git a/paimon-web-ui/vite.config.ts
b/paimon-web-ui/__unconfig_vite.config.ts
similarity index 71%
copy from paimon-web-ui/vite.config.ts
copy to paimon-web-ui/__unconfig_vite.config.ts
index 859f6c5..d6c159e 100644
--- a/paimon-web-ui/vite.config.ts
+++ b/paimon-web-ui/__unconfig_vite.config.ts
@@ -1,3 +1,7 @@
+
+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
@@ -15,7 +19,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-import { fileURLToPath, URL } from 'node:url'
+import { URL, fileURLToPath } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
@@ -23,9 +27,10 @@ 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/
-export default defineConfig({
+const __unconfig_default = defineConfig({
server: {
proxy: {
'/mock': {
@@ -35,8 +40,8 @@ export default defineConfig({
'/api': {
target: 'http://127.0.0.1:10088',
changeOrigin: true,
- }
- }
+ },
+ },
},
plugins: [
vue(),
@@ -51,32 +56,34 @@ export default defineConfig({
'useDialog',
'useMessage',
'useNotification',
- 'useLoadingBar'
- ]
- }
+ 'useLoadingBar',
+ ],
+ },
],
dts: './auto-imports.d.ts',
dirs: [
'./src/composables',
],
eslintrc: {
- enabled: false
- }
+ enabled: false,
+ },
}),
Components({
- resolvers: [NaiveUiResolver()]
- })
+ resolvers: [NaiveUiResolver()],
+ }),
],
css: {
postcss: {
plugins: [
- require("autoprefixer")
- ]
- }
+ autoprefixer(),
+ ],
+ },
},
resolve: {
alias: {
- '@': fileURLToPath(new URL('./src', import.meta.url))
- }
- }
+ '@': 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/mock/modules/index.js
b/paimon-web-ui/mock/modules/index.js
index 5895c6b..6497be2 100644
--- a/paimon-web-ui/mock/modules/index.js
+++ b/paimon-web-ui/mock/modules/index.js
@@ -16,7 +16,9 @@ specific language governing permissions and limitations
under the License. */
const metadata = require(`./metadata`)
+const system = require(`./system`)
module.exports = {
- metadata
+ metadata,
+ system
}
diff --git a/paimon-web-ui/mock/modules/system.js
b/paimon-web-ui/mock/modules/system.js
index 49b6e9e..007f728 100644
--- a/paimon-web-ui/mock/modules/system.js
+++ b/paimon-web-ui/mock/modules/system.js
@@ -51,6 +51,89 @@ module.exports = (mockUtil) => ({
]
}),
+ '/menu/treeselect': mockUtil({
+ "code": 200,
+ "msg": "Successfully",
+ "data": [
+ {
+ "id": 1,
+ "label": "system",
+ "children": [
+ {
+ "id": 300,
+ "label": "menu_manager",
+ "children": [
+ {
+ "id": 3000,
+ "label": "menu_query"
+ },
+ {
+ "id": 3001,
+ "label": "menu_add"
+ },
+ {
+ "id": 3002,
+ "label": "menu_update"
+ },
+ {
+ "id": 3003,
+ "label": "menu_delete"
+ }
+ ]
+ },
+ {
+ "id": 100,
+ "label": "user_manager",
+ "children": [
+ {
+ "id": 1000,
+ "label": "user_query"
+ },
+ {
+ "id": 1001,
+ "label": "user_add"
+ },
+ {
+ "id": 1002,
+ "label": "user_update"
+ },
+ {
+ "id": 1003,
+ "label": "user_delete"
+ },
+ {
+ "id": 1004,
+ "label": "user_reset"
+ }
+ ]
+ },
+ {
+ "id": 200,
+ "label": "role_manager",
+ "children": [
+ {
+ "id": 2000,
+ "label": "role_query"
+ },
+ {
+ "id": 2001,
+ "label": "role_add"
+ },
+ {
+ "id": 2002,
+ "label": "role_update"
+ },
+ {
+ "id": 2003,
+ "label": "role_delete"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }),
+
'/menu/roleMenuTreeselect/:roleId': mockUtil({
"code": 200,
"msg": "Successfully",
@@ -135,5 +218,72 @@ module.exports = (mockUtil) => ({
}
]
}
- })
+ }),
+
+ '/user/list': mockUtil({
+ "total": "2",
+ "success": true,
+ "data": [
+ {
+ "id": 1,
+ "username": "admin",
+ "nickname": "admin",
+ "userType": "0",
+ "mobile": "13400263598",
+ "email": "[email protected]",
+ "enabled": true,
+ "createTime": "2024-05-11T13:49:27",
+ "updateTime": null,
+ "roles": null
+ },
+ {
+ "id": 2,
+ "username": "common",
+ "nickname": "common",
+ "userType": "0",
+ "mobile": "13400263598",
+ "email": "[email protected]",
+ "enabled": true,
+ "createTime": "2024-05-11T13:49:27",
+ "updateTime": null,
+ "roles": null
+ }
+ ]
+ }),
+
+ 'post /user': mockUtil({
+ "code": 200,
+ "msg": "Successfully",
+ "data": null
+ }),
+
+ 'put /user': mockUtil({
+ "code": 200,
+ "msg": "Successfully",
+ "data": null
+ }),
+
+ 'delete /user/:userid': mockUtil({
+ "code": 200,
+ "msg": "Successfully",
+ "data": null
+ }),
+
+ 'post /user/change/password': mockUtil({
+ "code": 200,
+ "msg": "Successfully",
+ "data": null
+ }),
+
+ 'put /user/changeStatus': mockUtil({
+ "code": 200,
+ "msg": "Successfully",
+ "data": null
+ }),
+
+ 'post /user/allocate': mockUtil({
+ "code": 200,
+ "msg": "Successfully",
+ "data": null
+ }),
})
diff --git a/paimon-web-ui/src/api/models/role/index.ts
b/paimon-web-ui/src/api/models/role/index.ts
index da0ce32..ced5297 100644
--- a/paimon-web-ui/src/api/models/role/index.ts
+++ b/paimon-web-ui/src/api/models/role/index.ts
@@ -16,7 +16,7 @@ specific language governing permissions and limitations
under the License. */
import httpRequest from '../../request'
-import type { Role, RoleDTO, RoleDetail, RoleMenu, RoleParams } from
'./types/role'
+import type { Role, RoleDTO, RoleDetail, RoleMenu, RoleParams } from './types'
import type { ResponseOptions } from '@/api/types'
/**
diff --git a/paimon-web-ui/src/api/models/role/types/role.ts
b/paimon-web-ui/src/api/models/role/types.ts
similarity index 100%
copy from paimon-web-ui/src/api/models/role/types/role.ts
copy to paimon-web-ui/src/api/models/role/types.ts
diff --git a/paimon-web-ui/src/api/models/role/index.ts
b/paimon-web-ui/src/api/models/user/index.ts
similarity index 51%
copy from paimon-web-ui/src/api/models/role/index.ts
copy to paimon-web-ui/src/api/models/user/index.ts
index da0ce32..a855187 100644
--- a/paimon-web-ui/src/api/models/role/index.ts
+++ b/paimon-web-ui/src/api/models/user/index.ts
@@ -16,56 +16,42 @@ specific language governing permissions and limitations
under the License. */
import httpRequest from '../../request'
-import type { Role, RoleDTO, RoleDetail, RoleMenu, RoleParams } from
'./types/role'
+import type { User, UserDTO, UserParams } from './types'
import type { ResponseOptions } from '@/api/types'
/**
- * # create a role
+ * # List user
*/
-export function createRole() {
- return httpRequest.createHooks!<unknown, RoleDTO>({
- url: '/role',
- method: 'post',
+export function getUserList() {
+ return httpRequest.createHooks!<ResponseOptions<User[]>, UserParams>({
+ url: '/user/list',
+ method: 'get',
})
}
/**
- * # update a role
+ * # Create user
*/
-export function updateRole() {
- return httpRequest.createHooks!<unknown, RoleDTO>({
- url: '/role',
- method: 'put',
+export function createUser() {
+ return httpRequest.createHooks!<unknown, UserDTO>({
+ url: '/user',
+ method: 'post',
})
}
/**
- * # delete a role
- */
-export function deleteRole(roleId: number) {
- return httpRequest.delete!<unknown, RoleDTO>(`/role/${roleId}`)
-}
-
-/**
- * # permission tree
+ * # Update user
*/
-export function getPermissionTree() {
- return httpRequest.get!<string,
ResponseOptions<RoleMenu[]>>(`/menu/treeselect`)
-}
-
-/**
- * # permission tree by role Id
- */
-export function getPermissionByRoleId(roleId: number) {
- return httpRequest.get!<string,
ResponseOptions<RoleDetail>>(`/menu/roleMenuTreeselect/${roleId}`)
+export function updateUser() {
+ return httpRequest.createHooks!<unknown, UserDTO>({
+ url: '/user',
+ method: 'put',
+ })
}
/**
- * # List roles
+ * # delete a user
*/
-export function listRoles() {
- return httpRequest.createHooks!<ResponseOptions<Role[]>, RoleParams>({
- url: '/role/list',
- method: 'get',
- })
+export function deleteUser(userId: number) {
+ return httpRequest.delete!<unknown, UserDTO>(`/user/${userId}`)
}
diff --git a/paimon-web-ui/src/api/models/role/types/role.ts
b/paimon-web-ui/src/api/models/user/types.ts
similarity index 62%
rename from paimon-web-ui/src/api/models/role/types/role.ts
rename to paimon-web-ui/src/api/models/user/types.ts
index 828ef7b..6ef5792 100644
--- a/paimon-web-ui/src/api/models/role/types/role.ts
+++ b/paimon-web-ui/src/api/models/user/types.ts
@@ -1,3 +1,4 @@
+
/* 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
@@ -15,44 +16,35 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-export interface RoleDetail {
- checkedKeys: number[]
- menus: RoleMenu[]
-}
-
-export interface RoleMenu {
- id: number
- label: string
- children: RoleMenu[]
-}
-
-export interface Role {
+export interface User {
id: number
- createTime: string
- updateTime?: string
- roleName: string
- roleKey: string
- sort: number
+ username: string
+ nickname: string
+ userType: string
+ mobile?: string
+ email?: string
enabled: boolean
- isDelete: boolean
- admin?: boolean
- remark?: string
- flag: boolean
- menuIds: null
- permissions: null
+ createTime: string
+ updateTime: string
+ roles?: []
}
-export interface RoleParams {
- roleName?: string
- currentPage: number
+export interface UserParams {
+ username?: string
+ pageNum: number
pageSize: number
}
-export interface RoleDTO {
+export interface UserDTO {
id?: number
- roleName: string
- roleKey: string
- enabled: boolean
- remark?: string
- menuIds: number[]
+ username: string
+ password: string
+ nickname?: string
+ mobile?: string
+ email?: string
+ enabled: true
+ roleIds?: string[]
+ createTime?: string
+ updateTime?: string
+ roles?: []
}
diff --git a/paimon-web-ui/src/locales/en/modules/system.ts
b/paimon-web-ui/src/locales/en/modules/system.ts
index 52cf5a1..7fe5b7f 100644
--- a/paimon-web-ui/src/locales/en/modules/system.ts
+++ b/paimon-web-ui/src/locales/en/modules/system.ts
@@ -21,6 +21,13 @@ const user = {
nickname: 'Nickname',
mobile: 'Mobile',
email: 'Email',
+ password: 'Password',
+
+ create: 'Create User',
+ update: 'Update User',
+
+ enabled: 'Enabled',
+ roleIds: 'Role',
}
const role = {
diff --git a/paimon-web-ui/src/locales/zh/modules/system.ts
b/paimon-web-ui/src/locales/zh/modules/system.ts
index 7e3630a..1530974 100644
--- a/paimon-web-ui/src/locales/zh/modules/system.ts
+++ b/paimon-web-ui/src/locales/zh/modules/system.ts
@@ -21,6 +21,13 @@ const user = {
nickname: '昵称',
mobile: '手机号',
email: '邮箱',
+ password: '密码',
+
+ create: '新增用户',
+ update: '更新用户',
+
+ enabled: '是否启用',
+ roleIds: '角色',
}
const role = {
diff --git a/paimon-web-ui/src/store/permission/index.ts
b/paimon-web-ui/src/store/permission/index.ts
index ea32ae8..496a964 100644
--- a/paimon-web-ui/src/store/permission/index.ts
+++ b/paimon-web-ui/src/store/permission/index.ts
@@ -17,7 +17,7 @@ under the License. */
import { getPermissionTree } from '@/api/models/role'
-import type { RoleMenu } from '@/api/models/role/types/role'
+import type { RoleMenu } from '@/api/models/role/types'
export const usePermissionStore = defineStore('permission', () => {
const permissionList = ref<RoleMenu[]>([])
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 9f971cc..2d5075c 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
@@ -16,7 +16,7 @@ specific language governing permissions and limitations
under the License. */
import type { FormRules, TreeOption } from 'naive-ui'
-import type { RoleDTO } from '@/api/models/role/types/role'
+import type { RoleDTO } from '@/api/models/role/types'
import { usePermissionStore } from '@/store/permission'
const props = {
diff --git a/paimon-web-ui/src/views/system/role/index.tsx
b/paimon-web-ui/src/views/system/role/index.tsx
index 0d1db4b..9b4e19f 100644
--- a/paimon-web-ui/src/views/system/role/index.tsx
+++ b/paimon-web-ui/src/views/system/role/index.tsx
@@ -23,7 +23,7 @@ import RoleForm from './components/role-form'
import RoleDetail from './components/role-detail'
import RoleDelete from './components/role-delete'
import { createRole, getPermissionByRoleId, updateRole } from
'@/api/models/role'
-import type { Role, RoleDTO } from '@/api/models/role/types/role'
+import type { Role, RoleDTO } from '@/api/models/role/types'
export default defineComponent({
name: 'RolePage',
diff --git a/paimon-web-ui/src/views/system/role/use-table.ts
b/paimon-web-ui/src/views/system/role/use-table.ts
index 12f3a32..0a7d9e7 100644
--- a/paimon-web-ui/src/views/system/role/use-table.ts
+++ b/paimon-web-ui/src/views/system/role/use-table.ts
@@ -37,7 +37,7 @@ export function useTable() {
const [roleList, useRoleList, { loading }] = listRoles()
- const getTableData = () => {
+ function getTableData() {
const params = {
roleName: tableVariables.searchForm.roleName,
currentPage: tableVariables.pagination.page,
diff --git
a/paimon-web-ui/src/views/system/user/components/user-delete/index.tsx
b/paimon-web-ui/src/views/system/user/components/user-delete/index.tsx
new file mode 100644
index 0000000..f03f963
--- /dev/null
+++ b/paimon-web-ui/src/views/system/user/components/user-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 { deleteUser } from '@/api/models/user'
+
+export default defineComponent({
+ props: {
+ userId: {
+ type: Number,
+ },
+ onDelete: Function,
+ },
+ setup(props) {
+ const onDelete = async () => {
+ (props.userId || props.userId === 0) && await deleteUser(props.userId)
+ 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-form/index.tsx
b/paimon-web-ui/src/views/system/user/components/user-form/index.tsx
similarity index 58%
copy from paimon-web-ui/src/views/system/role/components/role-form/index.tsx
copy to paimon-web-ui/src/views/system/user/components/user-form/index.tsx
index 9f971cc..5bd86ae 100644
--- a/paimon-web-ui/src/views/system/role/components/role-form/index.tsx
+++ b/paimon-web-ui/src/views/system/user/components/user-form/index.tsx
@@ -15,9 +15,10 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-import type { FormRules, TreeOption } from 'naive-ui'
-import type { RoleDTO } from '@/api/models/role/types/role'
-import { usePermissionStore } from '@/store/permission'
+import type { FormItemRule, TreeOption } from 'naive-ui'
+
+import type { UserDTO } from '@/api/models/user/types'
+import { listRoles } from '@/api/models/role'
const props = {
'visible': {
@@ -34,7 +35,7 @@ const props = {
},
'formValue': {
- type: Object as PropType<RoleDTO>,
+ type: Object as PropType<UserDTO>,
default: () => ({
roleName: '',
roleKey: '',
@@ -43,59 +44,59 @@ const props = {
menuIds: [],
}),
},
- 'onUpdate:formValue': [Function, Object] as PropType<((value: RoleDTO) =>
void) | undefined>,
+ 'onUpdate:formValue': [Function, Object] as PropType<((value: UserDTO) =>
void) | undefined>,
'onConfirm': Function,
}
export default defineComponent({
- name: 'RoleForm',
+ name: 'UserForm',
props,
setup(props) {
const rules = {
- roleName: {
+ username: {
required: true,
trigger: ['blur', 'input'],
- message: 'role name required',
+ message: 'username required',
},
- roleKey: {
+ password: {
required: true,
trigger: ['blur', 'input'],
- message: 'role key required',
+ message: 'password required',
+ },
+ roleIds: {
+ required: true,
+ type: 'number',
+ trigger: ['blur', 'change'],
+ message: 'roleIds required',
},
- menuIds: {
+ mobile: {
required: true,
- trigger: ['blur'],
- validator: (_: FormRules, value: string) => {
- return new Promise<void>((resolve, reject) => {
- if (!value?.length)
- reject(new Error('menu ids required'))
- else
- resolve()
- })
+ trigger: ['input'],
+ validator: (rule: FormItemRule, value: string) => {
+ return /^1+[3,8]+\d{9}$/.test(value)
},
},
}
const { t } = useLocaleHooks()
- const permissionStore = usePermissionStore()
- const { permissionList } = storeToRefs(permissionStore)
+ const [roleList, useRoleList] = listRoles()
const formRef = ref()
+ watch(
+ () => props.visible,
+ (visible) => {
+ if (visible)
+ useRoleList()
+ },
+ )
+
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 () => {
+ async function handleConfirm() {
await formRef.value.validate()
props && props.onConfirm && props.onConfirm()
handleCloseModal()
@@ -104,11 +105,13 @@ export default defineComponent({
function resetState() {
props['onUpdate:formValue'] && props['onUpdate:formValue']({
- roleName: '',
- roleKey: '',
+ username: '',
+ nickname: '',
+ password: '',
enabled: true,
- remark: '',
- menuIds: [],
+ mobile: '',
+ email: '',
+ roleIds: undefined,
})
}
@@ -116,10 +119,8 @@ export default defineComponent({
...toRefs(props),
formRef,
rules,
- permissionTree: permissionList,
+ roleList,
handleCloseModal,
- renderLabel,
- onUpdateMenuIds,
handleConfirm,
t,
}
@@ -138,37 +139,34 @@ export default defineComponent({
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 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.role.role_key')}
path="roleKey">
- <n-input v-model:value={this.formValue.roleKey} />
+ <n-form-item label={this.t('system.user.nickname')}
path="nickname">
+ <n-input v-model:value={this.formValue.nickname} />
</n-form-item>
- <n-form-item label={this.t('system.role.enabled')}
path="enabled">
- <n-switch v-model:value={this.formValue.enabled} />
+ {
+ 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>
- <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 label={this.t('system.user.email')} path="email">
+ <n-input v-model:value={this.formValue.email} />
+ </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.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 label={this.t('system.user.roleIds')}
path="roleIds">
+ <n-select
+ v-model:value={this.formValue.roleIds}
+ options={this.roleList || []}
+ value-field="id"
+ label-field="roleName"
/>
</n-form-item>
</n-form>
diff --git a/paimon-web-ui/src/views/system/user/index.tsx
b/paimon-web-ui/src/views/system/user/index.tsx
index 45360d3..29ce352 100644
--- a/paimon-web-ui/src/views/system/user/index.tsx
+++ b/paimon-web-ui/src/views/system/user/index.tsx
@@ -15,16 +15,201 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
+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 styles from './index.module.scss'
+import { createUser, getUserList, updateUser } from '@/api/models/user'
+
+import type { User, UserDTO } from '@/api/models/user/types'
+
export default defineComponent({
name: 'UserPage',
setup() {
- return {}
+ const { t } = useLocaleHooks()
+ const rowKey = (rowData: any) => rowData.id
+ const message = useMessage()
+
+ const columns: TableColumns<UserDTO> = [
+ {
+ title: () => t('system.user.username'),
+ key: 'username',
+ },
+ {
+ title: () => t('system.user.nickname'),
+ key: 'nickname',
+ },
+ {
+ title: () => t('system.user.mobile'),
+ key: 'mobile',
+ },
+ {
+ title: () => t('system.user.enabled'),
+ key: 'enabled',
+ render: (row: UserDTO) => {
+ return row.enabled ? t('common.yes') : t('common.no')
+ },
+ },
+ {
+ title: () => t('common.create_time'),
+ key: 'createTime',
+ render: (row: UserDTO) => {
+ return row?.createTime ? dayjs(row?.createTime).format('YYYY-MM-DD
HH:mm') : '-'
+ },
+ },
+ {
+ title: () => t('common.update_time'),
+ key: 'updateTime',
+ render: (row: UserDTO) => {
+ return row?.updateTime ? dayjs(row?.updateTime).format('YYYY-MM-DD
HH:mm') : '-'
+ },
+ },
+ {
+ title: () => t('common.action'),
+ key: 'actions',
+ resizable: true,
+ render: (row: UserDTO) => {
+ return (
+ <n-space>
+ <n-button onClick={() => handleUpdateModal(row)} strong
secondary circle>
+ {{
+ icon: () => <n-icon component={EditOutlined} />,
+ }}
+ </n-button>
+ <UserDelete userId={row?.id} onDelete={getTableData} />
+ </n-space>
+ )
+ },
+ },
+ ]
+
+ const [userList, useAccountList, { loading }] = getUserList()
+ const [, createFetch, { loading: createLoading }] = createUser()
+ const [, updateFetch, { loading: updateLoading }] = updateUser()
+
+ const formType = ref<'create' | 'update'>('create')
+ const formVisible = ref(false)
+
+ const formValue = ref<UserDTO>({
+ username: '',
+ password: '',
+ nickname: '',
+ enabled: true,
+ mobile: '',
+ email: '',
+ roleIds: [],
+ })
+
+ onMounted(getTableData)
+
+ function handleCreateModal() {
+ formType.value = 'create'
+ formVisible.value = true
+ }
+
+ async function handleUpdateModal(user: UserDTO) {
+ formType.value = 'update'
+
+ delete user.createTime
+ delete user.updateTime
+ delete user.roles
+
+ formValue.value = { ...user }
+ formVisible.value = true
+ }
+
+ const tableVariables = reactive({
+ searchForm: {
+ username: '',
+ },
+ pagination: {
+ showQuickJumper: true,
+ showSizePicker: true,
+ pageSize: 10,
+ page: 1,
+ itemCount: 0,
+ onUpdatePage: (page: number) => {
+ tableVariables.pagination.page = page
+ getTableData()
+ },
+ },
+ })
+
+ function getTableData() {
+ const params = {
+ username: tableVariables.searchForm.username,
+ pageNum: tableVariables.pagination.page,
+ pageSize: tableVariables.pagination.pageSize,
+ }
+ useAccountList({ params })
+ }
+
+ const modelLoading = computed(() => createLoading.value ||
updateLoading.value)
+
+ async function onConfirm() {
+ const fn = formType.value === 'create' ? createFetch : updateFetch
+
+ const params = { ...toRaw(formValue.value) }
+
+ if (!Array.isArray(params.roleIds))
+ params.roleIds = [params.roleIds || '']
+
+ await fn({
+ params,
+ })
+
+ message.success(t(`Successfully`))
+
+ formVisible.value = false
+ getTableData()
+ }
+
+ return {
+ t,
+ rowKey,
+ modelLoading,
+ columns,
+ loading,
+ userList,
+ ...toRefs(tableVariables),
+
+ formType,
+ formVisible,
+ formValue,
+ handleCreateModal,
+ onConfirm,
+ }
},
render() {
return (
- <div class={styles.container}>UserPage</div>
+ <div class={styles.container}>
+ <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-space>
+ <n-data-table
+ columns={this.columns}
+ data={this.userList || []}
+ pagination={this.pagination}
+ loading={this.loading}
+ remote
+ rowKey={this.rowKey}
+ />
+ </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>
)
},
})
diff --git a/paimon-web-ui/vite.config.ts b/paimon-web-ui/vite.config.ts
index 859f6c5..3e34e2b 100644
--- a/paimon-web-ui/vite.config.ts
+++ b/paimon-web-ui/vite.config.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 { fileURLToPath, URL } from 'node:url'
+import { URL, fileURLToPath } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
@@ -23,6 +23,7 @@ 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/
export default defineConfig({
@@ -35,8 +36,8 @@ export default defineConfig({
'/api': {
target: 'http://127.0.0.1:10088',
changeOrigin: true,
- }
- }
+ },
+ },
},
plugins: [
vue(),
@@ -51,32 +52,32 @@ export default defineConfig({
'useDialog',
'useMessage',
'useNotification',
- 'useLoadingBar'
- ]
- }
+ 'useLoadingBar',
+ ],
+ },
],
dts: './auto-imports.d.ts',
dirs: [
'./src/composables',
],
eslintrc: {
- enabled: false
- }
+ enabled: false,
+ },
}),
Components({
- resolvers: [NaiveUiResolver()]
- })
+ resolvers: [NaiveUiResolver()],
+ }),
],
css: {
postcss: {
plugins: [
- require("autoprefixer")
- ]
- }
+ autoprefixer(),
+ ],
+ },
},
resolve: {
alias: {
- '@': fileURLToPath(new URL('./src', import.meta.url))
- }
- }
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
+ },
+ },
})