Browse Source
commit c6577279b8b1c94c9737d32a9ca1ba3437c5979b Author: K <1175047471@qq.com> Date: Fri Apr 12 09:40:22 2024 +0800 chore: 取消 token 刷新 commit 349b8176f28e294fbde7dd8a4c50f064785fdf9d Author: K <1175047471@qq.com> Date: Thu Apr 11 21:20:39 2024 +0800 chore: cleanup commit 309887ade9b8a8bb4efd35f09983ddcd3afb76df Author: lipenghui <mrkezhi@163.com> Date: Thu Apr 11 21:15:02 2024 +0800 fix: 岗位查看 commit 0f4f83dd51c2ed454fc1ee3561f315bc7e35dc6a Author: lipenghui <mrkezhi@163.com> Date: Thu Apr 11 21:13:47 2024 +0800 feat: 租户管理 commit 04e5d8d685616a601fd1d3b82ab18c4442536de8 Author: K <1175047471@qq.com> Date: Thu Apr 11 21:09:50 2024 +0800 chore: fix useinfo 未更新 commit 75ed18056c878c445ede439f27a1df2af41498dd Author: K <1175047471@qq.com> Date: Thu Apr 11 20:58:47 2024 +0800 chore: 修改首页地址 commit ef93ffc03d6f06f21b996f873b2b36e625c58a89 Author: K <1175047471@qq.com> Date: Thu Apr 11 20:52:27 2024 +0800 feat: 个人中心 commit 476a436f8654b8cd72fefd2105df2ecca7d517f7 Author: K <1175047471@qq.com> Date: Thu Apr 11 19:53:41 2024 +0800 wip: 个人中心 commit e5704dc3ad9dea20af45457dedc72ba65eb9d732 Author: K <1175047471@qq.com> Date: Thu Apr 11 19:36:55 2024 +0800 feat: 用户管理 commit e22200135c29ddbb17cbaae2d857c46478a9d9f7 Author: lipenghui <mrkezhi@163.com> Date: Thu Apr 11 17:42:25 2024 +0800 fix: 部门删除接口将ids改为id commit bbf42e206fad8a91a23edbbd68a3e2ce2d5ed59f Author: lipenghui <mrkezhi@163.com> Date: Thu Apr 11 17:40:20 2024 +0800 feat: 角色管理; commit c7e7e89bfe9100641811823846d1a0f48cd5f584 Author: lipenghui <mrkezhi@163.com> Date: Thu Apr 11 14:11:35 2024 +0800 feat: 岗位管理 commit 355d77d3cfc286664bd78bffbaca2766597e11c1 Author: K <1175047471@qq.com> Date: Thu Apr 11 11:12:33 2024 +0800 chore: 调整行政区划操作按钮位置 commit 9f7927b94625f7b1ffe74a3b396849631cea5bea Author: K <1175047471@qq.com> Date: Thu Apr 11 10:45:24 2024 +0800 wip: 用户管理 commit 87b423b14f5bf795b4f99d120feac6a06f3ac332 Author: K <1175047471@qq.com> Date: Thu Apr 11 09:16:25 2024 +0800 feat: 菜单管理 - 新增子项 commit f2fa565d939802896539e1e07cdf1bd3bfa1bb0e Author: lipenghui <mrkezhi@163.com> Date: Wed Apr 10 21:12:04 2024 +0800 chore: 菜单路由处理动态参数 commit 3a2c15b69ecd8243f6ca9237b1ff19dbb6d374fe Author: lipenghui <mrkezhi@163.com> Date: Wed Apr 10 21:08:23 2024 +0800 Revert "chore: 菜单路由处理动态参数" This reverts commit 3018cb399cd58e8e80285b83fc53ff9bdbd10e14. commit 9724ddc926c5efedc8f72ced29d31d61995b12a5 Author: lipenghui <mrkezhi@163.com> Date: Wed Apr 10 21:05:52 2024 +0800 chore: 菜单路由处理动态参数 commit 5bca711522df26d884669edd50eb53ce35c7ebd5 Author: lipenghui <mrkezhi@163.com> Date: Wed Apr 10 20:40:12 2024 +0800 fix: 删掉测试路由 commit 753306a1e4b7fdf17ac6337ca029a6f411baec1c Author: lipenghui <mrkezhi@163.com> Date: Wed Apr 10 20:11:03 2024 +0800 feat: 业务字典 commit 0f551cf23dc20f2750b823ff49864cfa1c55910e Author: lipenghui <mrkezhi@163.com> Date: Wed Apr 10 18:29:17 2024 +0800 fix: 系统字典-字典配置修改 commit 040923dd0eb3eb3846ddcf17efee9aa9fb2ca2da Author: lipenghui <mrkezhi@163.com> Date: Wed Apr 10 18:23:51 2024 +0800 feat: 系统字典 commit 1dfe6a3b221863fa792b8fc7432707c5fe009da2 Author: K <1175047471@qq.com> Date: Wed Apr 10 21:06:30 2024 +0800 fix: 菜单类型显示错误 commit c16cbdf807d7b8d44e2b0ff1b1310109c24c2d5d Author: K <1175047471@qq.com> Date: Wed Apr 10 20:57:36 2024 +0800 fix: 菜单无法跳转 commit f95c62d7faac99aa0700f14d46291fa34eafa0e2 Author: K <1175047471@qq.com> Date: Wed Apr 10 20:26:34 2024 +0800 feat: ImportModal 增加提示 commit 1e0af86b01d54e5ee8b89a5b48c8f2fb5276386f Author: K <1175047471@qq.com> Date: Wed Apr 10 20:21:37 2024 +0800 fix: 批量删除错误 commit 47a807a76e6cb006fb12ef8bf8e6a065f9e293b8 Author: K <1175047471@qq.com> Date: Wed Apr 10 20:19:00 2024 +0800 fix: 批量删除错误 commit 82248511dcb058a6885e64472a47df10bd43218d Author: K <1175047471@qq.com> Date: Wed Apr 10 20:07:28 2024 +0800 chore: cleanup commit fae351c1d131151738e42f82fbd0e98dd21f79bd Author: K <1175047471@qq.com> Date: Wed Apr 10 20:06:58 2024 +0800 feat: 行政区划 commit 9d370465faff6adac276f96e099a42ea015c4e4a Author: K <1175047471@qq.com> Date: Wed Apr 10 11:18:16 2024 +0800 fix: 菜单 icon 不应该是必选项 commit 68734b32e416ff6db4fe0e2748148e6867cd236a Author: K <1175047471@qq.com> Date: Wed Apr 10 10:59:32 2024 +0800 feat: 参数管理 - 批量删除 commit ff3a985a70f97db5ff169b64b2bfe997ce60360d Author: K <1175047471@qq.com> Date: Wed Apr 10 10:51:09 2024 +0800 feat: 参数管理 commit 6b1ec43e7a598a0140452eaae47223302cb6b345 Author: K <1175047471@qq.com> Date: Wed Apr 10 10:24:59 2024 +0800 wip: 菜单管理 commit 19ad299806d14f780ab52fc6d890224b63796750 Author: K <1175047471@qq.com> Date: Tue Apr 9 18:31:16 2024 +0800 wip: 系统管理 commit 8a02a8a64b90206a109d14ebdfc082b0ba1fcbdc Author: K <1175047471@qq.com> Date: Tue Apr 9 18:03:13 2024 +0800 wip: 系统管理main
85 changed files with 3732 additions and 568 deletions
@ -0,0 +1,48 @@
|
||||
import type { GetDictChildListParams, GetDictListParams, SystemDict, SystemDictChild, SystemDictTree } from './types' |
||||
import { defHttp } from '@/utils/http/axios' |
||||
|
||||
export function getDictList(params: GetDictListParams) { |
||||
return defHttp.get<PageResult<SystemDict>>({ |
||||
url: '/baymax-system/dict/parent-list', |
||||
params, |
||||
}) |
||||
} |
||||
|
||||
export function detailDict(id: string) { |
||||
return defHttp.get<SystemDict>({ |
||||
url: `/baymax-system/dict/detail?id=${id}`, |
||||
}) |
||||
} |
||||
|
||||
export function createDict(data: Partial<SystemDict>) { |
||||
return defHttp.post({ |
||||
url: '/baymax-system/dict/submit', |
||||
data, |
||||
}) |
||||
} |
||||
|
||||
export function updateDict(data: Partial<SystemDict>) { |
||||
return defHttp.post({ |
||||
url: '/baymax-system/dict/submit', |
||||
data, |
||||
}) |
||||
} |
||||
|
||||
export function deleteDict(ids: string[]) { |
||||
return defHttp.post({ |
||||
url: `/baymax-system/dict/remove?ids=${ids}`, |
||||
}) |
||||
} |
||||
|
||||
export function dictTree(code: string) { |
||||
return defHttp.get<SystemDictTree>({ |
||||
url: `/baymax-system/dict/tree?code=${code}`, |
||||
}) |
||||
} |
||||
|
||||
export function getDictChildList(params: GetDictChildListParams) { |
||||
return defHttp.get<PageResult<SystemDictChild>>({ |
||||
url: '/baymax-system/dict/child-list', |
||||
params, |
||||
}) |
||||
} |
@ -0,0 +1,36 @@
|
||||
export interface GetDictListParams extends PageParam { |
||||
} |
||||
|
||||
export interface GetDictChildListParams extends PageParam { |
||||
parentId: string |
||||
} |
||||
|
||||
export interface SystemDict { |
||||
id: string |
||||
code: string |
||||
dictKey: string |
||||
dictValue: string |
||||
hasChildren: boolean |
||||
isDeleted: number |
||||
isSealed: number |
||||
parentId: string |
||||
parentName: string |
||||
remark: string |
||||
sort: number |
||||
} |
||||
|
||||
export interface SystemDictChild extends SystemDict { |
||||
} |
||||
|
||||
export interface SystemDictTree extends SystemDictTreeItem { |
||||
children: SystemDictTree[] |
||||
} |
||||
|
||||
export interface SystemDictTreeItem { |
||||
id: string |
||||
hasChildren: boolean |
||||
key: string |
||||
parentId: string |
||||
title: string |
||||
value: string |
||||
} |
@ -0,0 +1,48 @@
|
||||
import type { GetDictbizChildListParams, GetDictbizListParams, SystemDictbiz, SystemDictbizChild, SystemDictbizTree } from './types' |
||||
import { defHttp } from '@/utils/http/axios' |
||||
|
||||
export function getDictbizList(params: GetDictbizListParams) { |
||||
return defHttp.get<PageResult<SystemDictbiz>>({ |
||||
url: '/baymax-system/dict-biz/parent-list', |
||||
params, |
||||
}) |
||||
} |
||||
|
||||
export function detailDictbiz(id: string) { |
||||
return defHttp.get<SystemDictbiz>({ |
||||
url: `/baymax-system/dict-biz/detail?id=${id}`, |
||||
}) |
||||
} |
||||
|
||||
export function createDictbiz(data: Partial<SystemDictbiz>) { |
||||
return defHttp.post({ |
||||
url: '/baymax-system/dict-biz/submit', |
||||
data, |
||||
}) |
||||
} |
||||
|
||||
export function updateDictbiz(data: Partial<SystemDictbiz>) { |
||||
return defHttp.post({ |
||||
url: '/baymax-system/dict-biz/submit', |
||||
data, |
||||
}) |
||||
} |
||||
|
||||
export function deleteDictbiz(ids: string[]) { |
||||
return defHttp.post({ |
||||
url: `/baymax-system/dict-biz/remove?ids=${ids}`, |
||||
}) |
||||
} |
||||
|
||||
export function dictbizTree(code: string) { |
||||
return defHttp.get<SystemDictbizTree>({ |
||||
url: `/baymax-system/dict-biz/tree?code=${code}`, |
||||
}) |
||||
} |
||||
|
||||
export function getDictbizChildList(params: GetDictbizChildListParams) { |
||||
return defHttp.get<PageResult<SystemDictbizChild>>({ |
||||
url: '/baymax-system/dict-biz/child-list', |
||||
params, |
||||
}) |
||||
} |
@ -0,0 +1,37 @@
|
||||
export interface GetDictbizListParams extends PageParam { |
||||
} |
||||
|
||||
export interface GetDictbizChildListParams extends PageParam { |
||||
parentId: string |
||||
} |
||||
|
||||
export interface SystemDictbiz { |
||||
id: string |
||||
code: string |
||||
dictKey: string |
||||
dictValue: string |
||||
hasChildren: boolean |
||||
isDeleted: number |
||||
isSealed: number |
||||
parentId: string |
||||
parentName: string |
||||
remark: string |
||||
sort: number |
||||
tenantId: string |
||||
} |
||||
|
||||
export interface SystemDictbizChild extends SystemDictbiz { |
||||
} |
||||
|
||||
export interface SystemDictbizTree extends SystemDictbizTreeItem { |
||||
children: SystemDictbizTree[] |
||||
} |
||||
|
||||
export interface SystemDictbizTreeItem { |
||||
id: string |
||||
hasChildren: boolean |
||||
key: string |
||||
parentId: string |
||||
title: string |
||||
value: string |
||||
} |
@ -0,0 +1,42 @@
|
||||
import type { GetPostListParams, SystemPost } from './types' |
||||
import { defHttp } from '@/utils/http/axios' |
||||
|
||||
export function getPostList(params: GetPostListParams) { |
||||
return defHttp.get<PageResult<SystemPost>>({ |
||||
url: '/baymax-system/post/list', |
||||
params, |
||||
}) |
||||
} |
||||
|
||||
export function detailPost(id: string) { |
||||
return defHttp.get<SystemPost>({ |
||||
url: `/baymax-system/post/detail?id=${id}`, |
||||
}) |
||||
} |
||||
|
||||
export function createPost(data: Partial<SystemPost>) { |
||||
return defHttp.post({ |
||||
url: '/baymax-system/post/submit', |
||||
data, |
||||
}) |
||||
} |
||||
|
||||
export function updatePost(data: Partial<SystemPost>) { |
||||
return defHttp.post({ |
||||
url: '/baymax-system/post/submit', |
||||
data, |
||||
}) |
||||
} |
||||
|
||||
export function deletePost(ids: string[]) { |
||||
return defHttp.post({ |
||||
url: `/baymax-system/post/remove?ids=${ids}`, |
||||
}) |
||||
} |
||||
|
||||
export function getPostTree(params: { tenantId: string }) { |
||||
return defHttp.get({ |
||||
url: '/baymax-system/post/select', |
||||
params, |
||||
}) |
||||
} |
@ -0,0 +1,21 @@
|
||||
export interface GetPostListParams extends PageParam { |
||||
} |
||||
|
||||
export interface SystemPost { |
||||
id: string |
||||
category: number |
||||
categoryName: string |
||||
createDept: string |
||||
createTime: string |
||||
createUser: string |
||||
isDeleted: number |
||||
postCode: string |
||||
postName: string |
||||
remark: string |
||||
sort: number |
||||
status: number |
||||
tenantId: string |
||||
tenantName?: string |
||||
updateTime: string |
||||
updateUser: string |
||||
} |
@ -0,0 +1,37 @@
|
||||
import type { Region } from './types' |
||||
import { defHttp } from '@/utils/http/axios' |
||||
|
||||
export function lazyGetRegionList(parentCode?: string) { |
||||
return defHttp.get<Region[]>({ |
||||
url: '/baymax-system/region/lazy-tree', |
||||
params: { |
||||
parentCode, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
export function getRegionDetail(code: string) { |
||||
return defHttp.get<Region>({ |
||||
url: `baymax-system/region/detail?code=${code}`, |
||||
}) |
||||
} |
||||
|
||||
export function updateRegion(data: Partial<Region>) { |
||||
return defHttp.post({ |
||||
url: '/baymax-system/region/submit', |
||||
data, |
||||
}) |
||||
} |
||||
|
||||
export function deleteRegion(id: string) { |
||||
return defHttp.post({ |
||||
url: `/baymax-system/region/remove?id=${id}`, |
||||
}) |
||||
} |
||||
|
||||
export function exportRegion() { |
||||
return defHttp.get({ |
||||
url: '/baymax-system/region/export-region', |
||||
responseType: 'blob', |
||||
}) |
||||
} |
@ -0,0 +1,15 @@
|
||||
export interface Region { |
||||
id: string |
||||
parentCode: string |
||||
subCode: string |
||||
code: string |
||||
name: string |
||||
sort: number |
||||
remark: string |
||||
parentId: string |
||||
title: string |
||||
value: string |
||||
hasChildren: boolean |
||||
parentName: string |
||||
regionLevel: number | string |
||||
} |
@ -1,56 +1,55 @@
|
||||
import type { GetRoleListParams, MenuTreeNode, Role } from './types' |
||||
import type { GetRoleListParams, GrantParams, MenuTreeNode, Role } from './types' |
||||
import { defHttp } from '@/utils/http/axios' |
||||
|
||||
export function lazyGetRoleList(params: GetRoleListParams) { |
||||
export function getRoleList(params: GetRoleListParams) { |
||||
return defHttp.get<Role[]>({ |
||||
url: '/system/role/lazy-list', |
||||
url: '/baymax-system/role/list', |
||||
params, |
||||
}) |
||||
} |
||||
|
||||
export function createRole(data: Partial<Role>) { |
||||
return defHttp.post({ |
||||
url: '/system/role/save', |
||||
url: '/baymax-system/role/submit', |
||||
data, |
||||
}) |
||||
} |
||||
|
||||
export function updateRole(data: Partial<Role>) { |
||||
return defHttp.post({ |
||||
url: '/system/role/update', |
||||
url: '/baymax-system/role/submit', |
||||
data, |
||||
}) |
||||
} |
||||
|
||||
export function deleteRole(id: string) { |
||||
return defHttp.post({ |
||||
url: `/system/role/delete?id=${id}`, |
||||
url: `/baymax-system/role/delete?id=${id}`, |
||||
}) |
||||
} |
||||
|
||||
export function getRoleTree(params?: { tenantId: string }) { |
||||
return defHttp.get<{ id: string, title: string }[]>({ |
||||
url: '/system/role/tree', |
||||
url: '/baymax-system/role/tree', |
||||
params, |
||||
}) |
||||
} |
||||
|
||||
export function getMenuTree() { |
||||
return defHttp.get<MenuTreeNode[]>({ |
||||
url: '/system/menu/grant-tree', |
||||
return defHttp.get<{ menu: MenuTreeNode[] }>({ |
||||
url: '/baymax-system/menu/grant-tree', |
||||
}) |
||||
} |
||||
|
||||
export function getMenuIdsByRole(roleId: string) { |
||||
return defHttp.get<string[]>({ |
||||
url: '/system/permission/list-role-menus', |
||||
params: { roleId }, |
||||
export function getRoleTreeKeys(roleIds: string) { |
||||
return defHttp.get<{ menu: string[] }>({ |
||||
url: `/baymax-system/menu/role-tree-keys?roleIds=${roleIds}`, |
||||
}) |
||||
} |
||||
|
||||
export function assignMenuToRole(data: { roleId: string, menuIds: string[] }) { |
||||
export function updateGrant(data: GrantParams) { |
||||
return defHttp.post({ |
||||
url: '/system/permission/assign-role-menu', |
||||
url: `/baymax-system/role/grant`, |
||||
data, |
||||
}) |
||||
} |
||||
|
@ -0,0 +1 @@
|
||||
export { default as ImportModal } from './src/ImportModal.vue' |
@ -0,0 +1,58 @@
|
||||
<script setup lang="ts"> |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import FileUpload from '@/components/Form/src/components/FileUpload.vue' |
||||
import { useGlobSetting } from '@/hooks/setting' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { downloadByData } from '@/utils/file/download' |
||||
import { defHttp } from '@/utils/http/axios' |
||||
import { noop } from '@/utils' |
||||
|
||||
const props = defineProps<{ |
||||
title?: string |
||||
uploadUrl: string |
||||
templateUrl?: string |
||||
hint?: string |
||||
}>() |
||||
const emit = defineEmits(['register', 'success']) |
||||
|
||||
const globSetting = useGlobSetting() |
||||
const urlPrefix = globSetting.urlPrefix |
||||
|
||||
const [register, { closeModal }] = useModalInner() |
||||
|
||||
function onUploadChange() { |
||||
useMessage().createMessage.success('导入成功') |
||||
emit('success') |
||||
closeModal() |
||||
} |
||||
|
||||
function downloadTemplate() { |
||||
defHttp.get({ |
||||
url: props.templateUrl!, |
||||
responseType: 'blob', |
||||
}, { isReturnNativeResponse: true }) |
||||
.then((res) => { |
||||
let filename: string = 'export-data.xlsx' |
||||
try { |
||||
filename = res.headers['content-disposition'].split(';filename=')[1] |
||||
filename = decodeURIComponent(filename) |
||||
} |
||||
catch { |
||||
} |
||||
downloadByData(res.data, filename) |
||||
}) |
||||
.catch(noop) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal :title="title || '数据导入'" @register="register"> |
||||
<FileUpload dragger :dragger-hint="hint" :show-upload-list="false" :upload-url="`${urlPrefix}${uploadUrl}`" @change="onUploadChange" /> |
||||
<div v-if="templateUrl" text="right" mt="10px"> |
||||
<a-button type="link" @click="downloadTemplate"> |
||||
下载模版 |
||||
</a-button> |
||||
</div> |
||||
<slot /> |
||||
</BasicModal> |
||||
</template> |
@ -1,40 +0,0 @@
|
||||
import type { AppRouteModule } from '@/router/types' |
||||
|
||||
import { LAYOUT } from '@/router/constant' |
||||
import { t } from '@/hooks/web/useI18n' |
||||
|
||||
const dashboard: AppRouteModule = { |
||||
path: '/dashboard', |
||||
name: 'Dashboard', |
||||
component: LAYOUT, |
||||
parentId: '0', |
||||
redirect: '/dashboard/analysis', |
||||
meta: { |
||||
orderNo: 10, |
||||
icon: 'i-ant-design:home-outlined', |
||||
title: t('routes.dashboard.dashboard'), |
||||
}, |
||||
children: [ |
||||
{ |
||||
path: 'analysis', |
||||
name: 'Analysis', |
||||
component: () => import('@/views/dashboard/analysis/index.vue'), |
||||
meta: { |
||||
// affix: true,
|
||||
title: t('routes.dashboard.analysis'), |
||||
icon: 'i-ant-design:bar-chart-outlined', |
||||
}, |
||||
}, |
||||
{ |
||||
path: 'workbench', |
||||
name: 'Workbench', |
||||
component: () => import('@/views/dashboard/workbench/index.vue'), |
||||
meta: { |
||||
title: t('routes.dashboard.workbench'), |
||||
icon: 'i-ant-design:appstore-outlined', |
||||
}, |
||||
}, |
||||
], |
||||
} |
||||
|
||||
export default dashboard |
@ -0,0 +1,28 @@
|
||||
import type { AppRouteModule } from '@/router/types' |
||||
|
||||
import { LAYOUT } from '@/router/constant' |
||||
import { t } from '@/hooks/web/useI18n' |
||||
|
||||
const profile: AppRouteModule = { |
||||
path: '/profile', |
||||
name: 'Profile', |
||||
component: LAYOUT, |
||||
parentId: '0', |
||||
redirect: '/profile/index', |
||||
meta: { |
||||
title: '', |
||||
hideMenu: true, |
||||
}, |
||||
children: [ |
||||
{ |
||||
path: 'index', |
||||
name: 'ProfileIndex', |
||||
component: () => import('@/views/base/profile/index.vue'), |
||||
meta: { |
||||
title: t('routes.basic.profile'), |
||||
}, |
||||
}, |
||||
], |
||||
} |
||||
|
||||
export default profile |
@ -0,0 +1,75 @@
|
||||
<script lang='ts' setup> |
||||
import { Spin } from 'ant-design-vue' |
||||
import { ref } from 'vue' |
||||
import CryptoJS from 'crypto-js' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { updatePassword } from '@/api/base/user' |
||||
import { noop } from '@/utils' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useUserStore } from '@/store/modules/user' |
||||
|
||||
const [register, { resetFields }] = useForm({ |
||||
schemas: [ |
||||
{ |
||||
field: 'oldPassword', |
||||
label: '原密码', |
||||
required: true, |
||||
component: 'InputPassword', |
||||
}, |
||||
{ |
||||
field: 'newPassword', |
||||
label: '新密码', |
||||
required: true, |
||||
component: 'InputPassword', |
||||
}, |
||||
{ |
||||
field: 'newPassword1', |
||||
label: '确认密码', |
||||
required: true, |
||||
component: 'InputPassword', |
||||
}, |
||||
], |
||||
baseColProps: { |
||||
span: 24, |
||||
}, |
||||
actionColOptions: { |
||||
span: 24, |
||||
style: { |
||||
textAlign: 'center', |
||||
}, |
||||
}, |
||||
labelWidth: 80, |
||||
submitButtonOptions: { |
||||
text: '保存', |
||||
}, |
||||
}) |
||||
|
||||
const { logout } = useUserStore() |
||||
|
||||
const loading = ref(false) |
||||
function handleSubmit(data) { |
||||
loading.value = true |
||||
|
||||
for (const key of Object.keys(data)) |
||||
data[key] = CryptoJS.MD5(data[key]).toString() |
||||
|
||||
updatePassword(data) |
||||
.then(() => { |
||||
resetFields() |
||||
useMessage().createMessage.success('保存成功, 请重新登陆') |
||||
logout() |
||||
}) |
||||
.catch(noop) |
||||
.finally(() => { |
||||
loading.value = false |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div w="1/3"> |
||||
<Spin :spinning="loading"> |
||||
<BasicForm @register="register" @submit="handleSubmit" /> |
||||
</Spin> |
||||
</div> |
||||
</template> |
@ -0,0 +1,94 @@
|
||||
<script lang='ts' setup> |
||||
import { Spin } from 'ant-design-vue' |
||||
import { ref } from 'vue' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { getUserInfo, updateUserInfo } from '@/api/base/user' |
||||
import { noop } from '@/utils' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useUserStore } from '@/store/modules/user' |
||||
|
||||
const [register, { setFieldsValue }] = useForm({ |
||||
schemas: [ |
||||
{ |
||||
field: 'avatar', |
||||
fields: ['id'], |
||||
label: '头像', |
||||
component: 'FileUpload', |
||||
componentProps: { |
||||
fileType: 'image', |
||||
maxCount: 1, |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'realName', |
||||
label: '姓名', |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
field: 'name', |
||||
label: '用户名', |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
field: 'phone', |
||||
label: '手机号', |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
field: 'email', |
||||
label: '邮箱', |
||||
component: 'Input', |
||||
}, |
||||
], |
||||
baseColProps: { |
||||
span: 24, |
||||
}, |
||||
actionColOptions: { |
||||
span: 24, |
||||
style: { |
||||
textAlign: 'center', |
||||
}, |
||||
}, |
||||
labelWidth: 50, |
||||
submitButtonOptions: { |
||||
text: '保存', |
||||
}, |
||||
}) |
||||
|
||||
const loading = ref(true) |
||||
getUserInfo() |
||||
.then((res) => { |
||||
loading.value = false |
||||
setFieldsValue(res) |
||||
}) |
||||
.catch(noop) |
||||
|
||||
const { setUserInfo, userInfo } = useUserStore() |
||||
function handleSubmit(data) { |
||||
loading.value = true |
||||
updateUserInfo(data) |
||||
.then(() => { |
||||
loading.value = false |
||||
useMessage().createMessage.success('保存成功') |
||||
setUserInfo({ |
||||
...userInfo!, |
||||
user: { |
||||
...userInfo!.user, |
||||
real_name: data.realName, |
||||
nick_name: data.name, |
||||
avatar: data.avatar, |
||||
phone: data.phone, |
||||
}, |
||||
}) |
||||
}) |
||||
.catch(noop) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div w="1/3"> |
||||
<Spin :spinning="loading"> |
||||
<BasicForm @register="register" @submit="handleSubmit" /> |
||||
</Spin> |
||||
</div> |
||||
</template> |
@ -0,0 +1,2 @@
|
||||
export { default as UserInfo } from './UserInfo.vue' |
||||
export { default as UpdatePassword } from './UpdatePassword.vue' |
@ -0,0 +1,19 @@
|
||||
<script lang='ts' setup> |
||||
import { Card, Tabs } from 'ant-design-vue' |
||||
import { UpdatePassword, UserInfo } from './components' |
||||
</script> |
||||
|
||||
<template> |
||||
<div p="12px"> |
||||
<Card> |
||||
<Tabs> |
||||
<Tabs.TabPane key="1" tab="个人信息"> |
||||
<UserInfo /> |
||||
</Tabs.TabPane> |
||||
<Tabs.TabPane key="2" tab="修改密码"> |
||||
<UpdatePassword /> |
||||
</Tabs.TabPane> |
||||
</Tabs> |
||||
</Card> |
||||
</div> |
||||
</template> |
@ -0,0 +1,57 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { getFormSchema } from './data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createDict, updateDict } from '@/api/system/dict' |
||||
import type { SystemDict } from '@/api/system/dict/types' |
||||
|
||||
defineOptions({ name: 'DictFormModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
|
||||
const isUpdate = ref(false) |
||||
const [registerForm, { setFieldsValue, validate }] = useForm({ |
||||
name: 'dict-form', |
||||
labelWidth: 100, |
||||
baseColProps: { span: 12 }, |
||||
schemas: getFormSchema(), |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: SystemDict) => { |
||||
isUpdate.value = true |
||||
setFieldsValue({ ...data }) |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate<SystemDict>() |
||||
setModalProps({ confirmLoading: true }) |
||||
await (isUpdate.value ? updateDict(values) : createDict(values)) |
||||
closeModal() |
||||
emit('success') |
||||
useMessage().createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
catch {} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal |
||||
width="55%" |
||||
:title="isUpdate ? t('action.edit') : t('action.create')" |
||||
:after-close="() => isUpdate = false" |
||||
@register="registerModal" |
||||
@ok="handleSubmit" |
||||
> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -0,0 +1,126 @@
|
||||
import { h } from 'vue' |
||||
import { Tag } from 'ant-design-vue' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import type { SystemDict } from '@/api/system/dict/types' |
||||
|
||||
export const columns: BasicColumn<SystemDict>[] = [ |
||||
{ |
||||
title: '字典编号', |
||||
dataIndex: 'code', |
||||
width: 180, |
||||
customRender: ({ record }) => { |
||||
const { code } = record |
||||
return h(Tag, { color: 'blue' }, () => code) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '字典名称', |
||||
dataIndex: 'dictValue', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '字典排序', |
||||
dataIndex: 'sort', |
||||
width: 50, |
||||
}, |
||||
{ |
||||
title: '封存', |
||||
dataIndex: 'isSealed', |
||||
width: 50, |
||||
customRender: ({ record }) => { |
||||
const { isSealed } = record |
||||
const str = isSealed === 0 ? '否' : '是' |
||||
return h(Tag, { color: 'blue' }, () => str) |
||||
}, |
||||
}, |
||||
|
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '字典编号', |
||||
field: 'code', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
{ |
||||
label: '字典名称', |
||||
field: 'dictValue', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
] |
||||
|
||||
export function getFormSchema(): FormSchema[] { |
||||
return [ |
||||
{ |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
field: 'dictKey', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: '-1', |
||||
}, |
||||
{ |
||||
field: 'isDeleted', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: 0, |
||||
}, |
||||
{ |
||||
field: 'parentId', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: '0', |
||||
}, |
||||
{ |
||||
field: 'parentName', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: '顶级', |
||||
}, |
||||
{ |
||||
label: '字典编号', |
||||
field: 'code', |
||||
required: true, |
||||
component: 'Input', |
||||
colProps: { |
||||
span: 24, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典名称', |
||||
field: 'dictValue', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '字典排序', |
||||
field: 'sort', |
||||
required: true, |
||||
component: 'InputNumber', |
||||
}, |
||||
{ |
||||
label: '封存', |
||||
field: 'isSealed', |
||||
required: true, |
||||
component: 'Switch', |
||||
defaultValue: 0, |
||||
componentProps: { |
||||
checkedChildren: '是', |
||||
unCheckedChildren: '否', |
||||
checkedValue: 1, |
||||
unCheckedValue: 0, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典备注', |
||||
field: 'remark', |
||||
required: false, |
||||
component: 'Input', |
||||
}, |
||||
] |
||||
} |
@ -0,0 +1,130 @@
|
||||
<script lang="ts" setup> |
||||
import { Space } from 'ant-design-vue' |
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue' |
||||
import { useRouter } from 'vue-router' |
||||
import { columns, searchFormSchema } from './data' |
||||
import DictFormModal from './DictFormModal.vue' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteDict, getDictList } from '@/api/system/dict' |
||||
import type { SystemDict } from '@/api/system/dict/types' |
||||
|
||||
// import { usePermission } from '@/hooks/web/usePermission' |
||||
|
||||
defineOptions({ name: 'SystemDict' }) |
||||
|
||||
const router = useRouter() |
||||
const { createMessage, createConfirm } = useMessage() |
||||
const [registerModal, { openModal }] = useModal<SystemDict>() |
||||
|
||||
// const { hasPermission } = usePermission() |
||||
|
||||
const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({ |
||||
api(params) { |
||||
return getDictList(params) |
||||
}, |
||||
columns, |
||||
formConfig: { |
||||
labelWidth: 80, |
||||
schemas: searchFormSchema, |
||||
}, |
||||
rowKey: 'id', |
||||
rowSelection: {}, |
||||
useSearchForm: true, |
||||
bordered: true, |
||||
canResize: false, |
||||
actionColumn: { |
||||
width: 160, |
||||
title: '操作', |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
// auth: ['user_edit', 'user_delete'], |
||||
}, |
||||
}) |
||||
|
||||
async function handleDelete(id?: string) { |
||||
const ids = id ? [id] : (getSelectRowKeys() as string[]) |
||||
if (!ids.length) |
||||
return createMessage.warning('请选择要删除的数据') |
||||
|
||||
async function execute() { |
||||
try { |
||||
await deleteDict(ids) |
||||
createMessage.success('删除成功') |
||||
reload() |
||||
clearSelectedRowKeys() |
||||
} |
||||
catch {} |
||||
} |
||||
|
||||
if (id) |
||||
return execute() |
||||
|
||||
createConfirm({ |
||||
iconType: 'warning', |
||||
title: '是否要删除选择的字典?', |
||||
onOk: execute, |
||||
}) |
||||
} |
||||
|
||||
function toPath(data: SystemDict) { |
||||
router.push({ |
||||
path: `/system/dict/setting/${data.id}`, |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable :api="async () => ([] as SystemDict[])" @register="registerTable"> |
||||
<template #tableTitle> |
||||
<Space> |
||||
<a-button type="primary" @click="openModal"> |
||||
<PlusOutlined /> |
||||
新增 |
||||
</a-button> |
||||
<a-button danger @click="handleDelete()"> |
||||
<DeleteOutlined /> |
||||
批量删除 |
||||
</a-button> |
||||
</Space> |
||||
</template> |
||||
|
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: 'i-ant-design:edit-outlined', |
||||
label: '修改', |
||||
onClick: () => openModal(true, record), |
||||
// auth: 'user_edit', |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:setting-outlined', |
||||
label: '字典配置', |
||||
// auth: 'user_delete', |
||||
onClick: () => toPath(record), |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:delete-outlined', |
||||
danger: true, |
||||
label: '删除', |
||||
// auth: 'user_delete', |
||||
popConfirm: { |
||||
title: '确定要删除数据吗?', |
||||
placement: 'left', |
||||
confirm: () => handleDelete(record.id), |
||||
}, |
||||
}, |
||||
|
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
|
||||
<DictFormModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -0,0 +1,61 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { getFormSchema } from './data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createDict, detailDict, updateDict } from '@/api/system/dict' |
||||
import type { SystemDict } from '@/api/system/dict/types' |
||||
|
||||
defineOptions({ name: 'SettingFormModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
|
||||
const isUpdate = ref(false) |
||||
const [registerForm, { setFieldsValue, validate }] = useForm({ |
||||
name: 'dict-setting-form', |
||||
labelWidth: 100, |
||||
baseColProps: { span: 12 }, |
||||
schemas: getFormSchema(), |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: SystemDict) => { |
||||
isUpdate.value = !!data.id |
||||
console.log('isUpdate.value', isUpdate.value) |
||||
|
||||
const res = await (isUpdate.value ? detailDict(data.id) : detailDict(data.parentId)) |
||||
const newData = isUpdate.value ? { ...data, parentName: res.parentName } : { code: res.code, parentName: res.dictValue } |
||||
setFieldsValue({ ...newData }) |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate<SystemDict>() |
||||
setModalProps({ confirmLoading: true }) |
||||
await (isUpdate.value ? updateDict(values) : createDict(values)) |
||||
closeModal() |
||||
emit('success') |
||||
useMessage().createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
catch {} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal |
||||
width="55%" |
||||
:title="isUpdate ? t('action.edit') : t('action.create')" |
||||
:after-close="() => isUpdate = false" |
||||
@register="registerModal" |
||||
@ok="handleSubmit" |
||||
> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -0,0 +1,132 @@
|
||||
import { h } from 'vue' |
||||
import { Tag } from 'ant-design-vue' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import type { SystemDict } from '@/api/system/dict/types' |
||||
|
||||
export const columns: BasicColumn<SystemDict>[] = [ |
||||
{ |
||||
title: '字典编号', |
||||
dataIndex: 'code', |
||||
width: 180, |
||||
customRender: ({ record }) => { |
||||
const { code } = record |
||||
return h(Tag, { color: 'blue' }, () => code) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '字典名称', |
||||
dataIndex: 'dictValue', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '字典键值', |
||||
dataIndex: 'dictKey', |
||||
width: 50, |
||||
}, |
||||
{ |
||||
title: '封存', |
||||
dataIndex: 'isSealed', |
||||
width: 50, |
||||
customRender: ({ record }) => { |
||||
const { isSealed } = record |
||||
const str = isSealed === 0 ? '否' : '是' |
||||
return h(Tag, { color: 'blue' }, () => str) |
||||
}, |
||||
}, |
||||
|
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '字典编号', |
||||
field: 'code', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
{ |
||||
label: '字典名称', |
||||
field: 'dictValue', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
] |
||||
|
||||
export function getFormSchema(): FormSchema[] { |
||||
return [ |
||||
{ |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
field: 'isDeleted', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: 0, |
||||
}, |
||||
{ |
||||
field: 'parentId', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: '0', |
||||
}, |
||||
{ |
||||
label: '字典编号', |
||||
field: 'code', |
||||
required: true, |
||||
component: 'Input', |
||||
componentProps: { |
||||
disabled: true, |
||||
}, |
||||
colProps: { |
||||
span: 24, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典名称', |
||||
field: 'dictValue', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '上级字典', |
||||
field: 'parentName', |
||||
required: true, |
||||
component: 'Input', |
||||
componentProps: { |
||||
disabled: true, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典键值', |
||||
field: 'dictKey', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '字典排序', |
||||
field: 'sort', |
||||
required: true, |
||||
component: 'InputNumber', |
||||
}, |
||||
{ |
||||
label: '封存', |
||||
field: 'isSealed', |
||||
required: true, |
||||
defaultValue: 0, |
||||
component: 'Switch', |
||||
componentProps: { |
||||
checkedChildren: '是', |
||||
unCheckedChildren: '否', |
||||
checkedValue: 1, |
||||
unCheckedValue: 0, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典备注', |
||||
field: 'remark', |
||||
required: false, |
||||
component: 'Input', |
||||
}, |
||||
] |
||||
} |
@ -0,0 +1,120 @@
|
||||
<script lang="ts" setup> |
||||
import { Space } from 'ant-design-vue' |
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue' |
||||
import { useRoute } from 'vue-router' |
||||
import { columns, searchFormSchema } from './data' |
||||
import SettingFormModal from './SettingFormModal.vue' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteDict, getDictChildList } from '@/api/system/dict' |
||||
import type { SystemDict } from '@/api/system/dict/types' |
||||
|
||||
// import { usePermission } from '@/hooks/web/usePermission' |
||||
|
||||
defineOptions({ name: 'SystemDictSetting' }) |
||||
|
||||
const route = useRoute() |
||||
const parentId = route.params.id as string |
||||
const { createMessage, createConfirm } = useMessage() |
||||
const [registerModal, { openModal }] = useModal<Partial<SystemDict>>() |
||||
|
||||
// const { hasPermission } = usePermission() |
||||
|
||||
const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({ |
||||
api(apiParams) { |
||||
apiParams.parentId = parentId |
||||
return getDictChildList(apiParams) |
||||
}, |
||||
columns, |
||||
formConfig: { |
||||
labelWidth: 80, |
||||
schemas: searchFormSchema, |
||||
}, |
||||
rowKey: 'id', |
||||
rowSelection: {}, |
||||
useSearchForm: true, |
||||
bordered: true, |
||||
canResize: false, |
||||
actionColumn: { |
||||
width: 100, |
||||
title: '操作', |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
// auth: ['user_edit', 'user_delete'], |
||||
}, |
||||
}) |
||||
|
||||
async function handleDelete(id?: string) { |
||||
const ids = id ? [id] : (getSelectRowKeys() as string[]) |
||||
if (!ids.length) |
||||
return createMessage.warning('请选择要删除的数据') |
||||
|
||||
async function execute() { |
||||
try { |
||||
await deleteDict(ids) |
||||
createMessage.success('删除成功') |
||||
reload() |
||||
clearSelectedRowKeys() |
||||
} |
||||
catch {} |
||||
} |
||||
|
||||
if (id) |
||||
return execute() |
||||
|
||||
createConfirm({ |
||||
iconType: 'warning', |
||||
title: '是否要删除选择的字典?', |
||||
onOk: execute, |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable :api="async () => ([] as SystemDict[])" @register="registerTable"> |
||||
<template #tableTitle> |
||||
<Space> |
||||
<a-button type="primary" @click="openModal(true, { parentId })"> |
||||
<PlusOutlined /> |
||||
新增 |
||||
</a-button> |
||||
<a-button danger @click="handleDelete()"> |
||||
<DeleteOutlined /> |
||||
批量删除 |
||||
</a-button> |
||||
</Space> |
||||
</template> |
||||
|
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: 'i-ant-design:edit-outlined', |
||||
label: '修改', |
||||
onClick: () => openModal(true, record), |
||||
// auth: 'user_edit', |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:delete-outlined', |
||||
danger: true, |
||||
label: '删除', |
||||
// auth: 'user_delete', |
||||
popConfirm: { |
||||
title: '确定要删除数据吗?', |
||||
placement: 'left', |
||||
confirm: () => handleDelete(record.id), |
||||
}, |
||||
}, |
||||
|
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
|
||||
<SettingFormModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -0,0 +1,57 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { getFormSchema } from './data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createDictbiz, updateDictbiz } from '@/api/system/dictbiz' |
||||
import type { SystemDictbiz } from '@/api/system/dictbiz/types' |
||||
|
||||
defineOptions({ name: 'DictbizFormModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
|
||||
const isUpdate = ref(false) |
||||
const [registerForm, { setFieldsValue, validate }] = useForm({ |
||||
name: 'dict-form', |
||||
labelWidth: 100, |
||||
baseColProps: { span: 12 }, |
||||
schemas: getFormSchema(), |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: SystemDictbiz) => { |
||||
isUpdate.value = true |
||||
setFieldsValue({ ...data }) |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate<SystemDictbiz>() |
||||
setModalProps({ confirmLoading: true }) |
||||
await (isUpdate.value ? updateDictbiz(values) : createDictbiz(values)) |
||||
closeModal() |
||||
emit('success') |
||||
useMessage().createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
catch {} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal |
||||
width="55%" |
||||
:title="isUpdate ? t('action.edit') : t('action.create')" |
||||
:after-close="() => isUpdate = false" |
||||
@register="registerModal" |
||||
@ok="handleSubmit" |
||||
> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -0,0 +1,133 @@
|
||||
import { h } from 'vue' |
||||
import { Tag } from 'ant-design-vue' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import type { SystemDictbiz } from '@/api/system/dictbiz/types' |
||||
import { getTenantId } from '@/utils/auth' |
||||
|
||||
export const columns: BasicColumn<SystemDictbiz>[] = [ |
||||
{ |
||||
title: '字典编号', |
||||
dataIndex: 'code', |
||||
width: 180, |
||||
customRender: ({ record }) => { |
||||
const { code } = record |
||||
return h(Tag, { color: 'blue' }, () => code) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '字典名称', |
||||
dataIndex: 'dictValue', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '字典排序', |
||||
dataIndex: 'sort', |
||||
width: 50, |
||||
}, |
||||
{ |
||||
title: '封存', |
||||
dataIndex: 'isSealed', |
||||
width: 50, |
||||
customRender: ({ record }) => { |
||||
const { isSealed } = record |
||||
const str = isSealed === 0 ? '否' : '是' |
||||
return h(Tag, { color: 'blue' }, () => str) |
||||
}, |
||||
}, |
||||
|
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '字典编号', |
||||
field: 'code', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
{ |
||||
label: '字典名称', |
||||
field: 'dictValue', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
] |
||||
|
||||
export function getFormSchema(): FormSchema[] { |
||||
return [ |
||||
{ |
||||
field: 'tenantId', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: getTenantId(), |
||||
}, |
||||
{ |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
field: 'dictKey', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: '-1', |
||||
}, |
||||
{ |
||||
field: 'isDeleted', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: 0, |
||||
}, |
||||
{ |
||||
field: 'parentId', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: '0', |
||||
}, |
||||
{ |
||||
field: 'parentName', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: '顶级', |
||||
}, |
||||
{ |
||||
label: '字典编号', |
||||
field: 'code', |
||||
required: true, |
||||
component: 'Input', |
||||
colProps: { |
||||
span: 24, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典名称', |
||||
field: 'dictValue', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '字典排序', |
||||
field: 'sort', |
||||
required: true, |
||||
component: 'InputNumber', |
||||
}, |
||||
{ |
||||
label: '封存', |
||||
field: 'isSealed', |
||||
required: true, |
||||
component: 'Switch', |
||||
defaultValue: 0, |
||||
componentProps: { |
||||
checkedChildren: '是', |
||||
unCheckedChildren: '否', |
||||
checkedValue: 1, |
||||
unCheckedValue: 0, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典备注', |
||||
field: 'remark', |
||||
required: false, |
||||
component: 'Input', |
||||
}, |
||||
] |
||||
} |
@ -0,0 +1,130 @@
|
||||
<script lang="ts" setup> |
||||
import { Space } from 'ant-design-vue' |
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue' |
||||
import { useRouter } from 'vue-router' |
||||
import { columns, searchFormSchema } from './data' |
||||
import DictbizFormModal from './DictbizFormModal.vue' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteDictbiz, getDictbizList } from '@/api/system/dictbiz' |
||||
import type { SystemDictbiz } from '@/api/system/dictbiz/types' |
||||
|
||||
// import { usePermission } from '@/hooks/web/usePermission' |
||||
|
||||
defineOptions({ name: 'SystemDictbiz' }) |
||||
|
||||
const router = useRouter() |
||||
const { createMessage, createConfirm } = useMessage() |
||||
const [registerModal, { openModal }] = useModal<SystemDictbiz>() |
||||
|
||||
// const { hasPermission } = usePermission() |
||||
|
||||
const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({ |
||||
api(params) { |
||||
return getDictbizList(params) |
||||
}, |
||||
columns, |
||||
formConfig: { |
||||
labelWidth: 80, |
||||
schemas: searchFormSchema, |
||||
}, |
||||
rowKey: 'id', |
||||
rowSelection: {}, |
||||
useSearchForm: true, |
||||
bordered: true, |
||||
canResize: false, |
||||
actionColumn: { |
||||
width: 160, |
||||
title: '操作', |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
// auth: ['user_edit', 'user_delete'], |
||||
}, |
||||
}) |
||||
|
||||
async function handleDelete(id?: string) { |
||||
const ids = id ? [id] : (getSelectRowKeys() as string[]) |
||||
if (!ids.length) |
||||
return createMessage.warning('请选择要删除的数据') |
||||
|
||||
async function execute() { |
||||
try { |
||||
await deleteDictbiz(ids) |
||||
createMessage.success('删除成功') |
||||
reload() |
||||
clearSelectedRowKeys() |
||||
} |
||||
catch {} |
||||
} |
||||
|
||||
if (id) |
||||
return execute() |
||||
|
||||
createConfirm({ |
||||
iconType: 'warning', |
||||
title: '是否要删除选择的字典?', |
||||
onOk: execute, |
||||
}) |
||||
} |
||||
|
||||
function toPath(data: SystemDictbiz) { |
||||
router.push({ |
||||
path: `/system/dictbiz/setting/${data.id}`, |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable :api="async () => ([] as SystemDictbiz[])" @register="registerTable"> |
||||
<template #tableTitle> |
||||
<Space> |
||||
<a-button type="primary" @click="openModal"> |
||||
<PlusOutlined /> |
||||
新增 |
||||
</a-button> |
||||
<a-button danger @click="handleDelete()"> |
||||
<DeleteOutlined /> |
||||
批量删除 |
||||
</a-button> |
||||
</Space> |
||||
</template> |
||||
|
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: 'i-ant-design:edit-outlined', |
||||
label: '修改', |
||||
onClick: () => openModal(true, record), |
||||
// auth: 'user_edit', |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:setting-outlined', |
||||
label: '字典配置', |
||||
// auth: 'user_delete', |
||||
onClick: () => toPath(record), |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:delete-outlined', |
||||
danger: true, |
||||
label: '删除', |
||||
// auth: 'user_delete', |
||||
popConfirm: { |
||||
title: '确定要删除数据吗?', |
||||
placement: 'left', |
||||
confirm: () => handleDelete(record.id), |
||||
}, |
||||
}, |
||||
|
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
|
||||
<DictbizFormModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -0,0 +1,61 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { getFormSchema } from './data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createDictbiz, detailDictbiz, updateDictbiz } from '@/api/system/dictbiz' |
||||
import type { SystemDict } from '@/api/system/dict/types' |
||||
|
||||
defineOptions({ name: 'SettingFormModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
|
||||
const isUpdate = ref(false) |
||||
const [registerForm, { setFieldsValue, validate }] = useForm({ |
||||
name: 'dict-setting-form', |
||||
labelWidth: 100, |
||||
baseColProps: { span: 12 }, |
||||
schemas: getFormSchema(), |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: SystemDict) => { |
||||
isUpdate.value = !!data.id |
||||
console.log('isUpdate.value', isUpdate.value) |
||||
|
||||
const res = await (isUpdate.value ? detailDictbiz(data.id) : detailDictbiz(data.parentId)) |
||||
const newData = isUpdate.value ? { ...data, parentName: res.parentName } : { code: res.code, parentName: res.dictValue } |
||||
setFieldsValue({ ...newData }) |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate<SystemDict>() |
||||
setModalProps({ confirmLoading: true }) |
||||
await (isUpdate.value ? updateDictbiz(values) : createDictbiz(values)) |
||||
closeModal() |
||||
emit('success') |
||||
useMessage().createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
catch {} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal |
||||
width="55%" |
||||
:title="isUpdate ? t('action.edit') : t('action.create')" |
||||
:after-close="() => isUpdate = false" |
||||
@register="registerModal" |
||||
@ok="handleSubmit" |
||||
> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -0,0 +1,139 @@
|
||||
import { h } from 'vue' |
||||
import { Tag } from 'ant-design-vue' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import type { SystemDictbiz } from '@/api/system/dictbiz/types' |
||||
import { getTenantId } from '@/utils/auth' |
||||
|
||||
export const columns: BasicColumn<SystemDictbiz>[] = [ |
||||
{ |
||||
title: '字典编号', |
||||
dataIndex: 'code', |
||||
width: 180, |
||||
customRender: ({ record }) => { |
||||
const { code } = record |
||||
return h(Tag, { color: 'blue' }, () => code) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '字典名称', |
||||
dataIndex: 'dictValue', |
||||
width: 180, |
||||
}, |
||||
{ |
||||
title: '字典键值', |
||||
dataIndex: 'dictKey', |
||||
width: 50, |
||||
}, |
||||
{ |
||||
title: '封存', |
||||
dataIndex: 'isSealed', |
||||
width: 50, |
||||
customRender: ({ record }) => { |
||||
const { isSealed } = record |
||||
const str = isSealed === 0 ? '否' : '是' |
||||
return h(Tag, { color: 'blue' }, () => str) |
||||
}, |
||||
}, |
||||
|
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '字典编号', |
||||
field: 'code', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
{ |
||||
label: '字典名称', |
||||
field: 'dictValue', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
] |
||||
|
||||
export function getFormSchema(): FormSchema[] { |
||||
return [ |
||||
{ |
||||
field: 'tenantId', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: getTenantId(), |
||||
}, |
||||
{ |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
field: 'isDeleted', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: 0, |
||||
}, |
||||
{ |
||||
field: 'parentId', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: '0', |
||||
}, |
||||
{ |
||||
label: '字典编号', |
||||
field: 'code', |
||||
required: true, |
||||
component: 'Input', |
||||
componentProps: { |
||||
disabled: true, |
||||
}, |
||||
colProps: { |
||||
span: 24, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典名称', |
||||
field: 'dictValue', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '上级字典', |
||||
field: 'parentName', |
||||
required: true, |
||||
component: 'Input', |
||||
componentProps: { |
||||
disabled: true, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典键值', |
||||
field: 'dictKey', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '字典排序', |
||||
field: 'sort', |
||||
required: true, |
||||
component: 'InputNumber', |
||||
}, |
||||
{ |
||||
label: '封存', |
||||
field: 'isSealed', |
||||
required: true, |
||||
defaultValue: 0, |
||||
component: 'Switch', |
||||
componentProps: { |
||||
checkedChildren: '是', |
||||
unCheckedChildren: '否', |
||||
checkedValue: 1, |
||||
unCheckedValue: 0, |
||||
}, |
||||
}, |
||||
{ |
||||
label: '字典备注', |
||||
field: 'remark', |
||||
required: false, |
||||
component: 'Input', |
||||
}, |
||||
] |
||||
} |
@ -0,0 +1,120 @@
|
||||
<script lang="ts" setup> |
||||
import { Space } from 'ant-design-vue' |
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue' |
||||
import { useRoute } from 'vue-router' |
||||
import { columns, searchFormSchema } from './data' |
||||
import SettingFormModal from './SettingFormModal.vue' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteDictbiz, getDictbizChildList } from '@/api/system/dictbiz' |
||||
import type { SystemDictbiz } from '@/api/system/dictbiz/types' |
||||
|
||||
// import { usePermission } from '@/hooks/web/usePermission' |
||||
|
||||
defineOptions({ name: 'SystemDictbizSetting' }) |
||||
|
||||
const route = useRoute() |
||||
const parentId = route.params.id as string |
||||
const { createMessage, createConfirm } = useMessage() |
||||
const [registerModal, { openModal }] = useModal<Partial<SystemDictbiz>>() |
||||
|
||||
// const { hasPermission } = usePermission() |
||||
|
||||
const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({ |
||||
api(apiParams) { |
||||
apiParams.parentId = parentId |
||||
return getDictbizChildList(apiParams) |
||||
}, |
||||
columns, |
||||
formConfig: { |
||||
labelWidth: 80, |
||||
schemas: searchFormSchema, |
||||
}, |
||||
rowKey: 'id', |
||||
rowSelection: {}, |
||||
useSearchForm: true, |
||||
bordered: true, |
||||
canResize: false, |
||||
actionColumn: { |
||||
width: 100, |
||||
title: '操作', |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
// auth: ['user_edit', 'user_delete'], |
||||
}, |
||||
}) |
||||
|
||||
async function handleDelete(id?: string) { |
||||
const ids = id ? [id] : (getSelectRowKeys() as string[]) |
||||
if (!ids.length) |
||||
return createMessage.warning('请选择要删除的数据') |
||||
|
||||
async function execute() { |
||||
try { |
||||
await deleteDictbiz(ids) |
||||
createMessage.success('删除成功') |
||||
reload() |
||||
clearSelectedRowKeys() |
||||
} |
||||
catch {} |
||||
} |
||||
|
||||
if (id) |
||||
return execute() |
||||
|
||||
createConfirm({ |
||||
iconType: 'warning', |
||||
title: '是否要删除选择的字典?', |
||||
onOk: execute, |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable :api="async () => ([] as SystemDictbiz[])" @register="registerTable"> |
||||
<template #tableTitle> |
||||
<Space> |
||||
<a-button type="primary" @click="openModal(true, { parentId })"> |
||||
<PlusOutlined /> |
||||
新增 |
||||
</a-button> |
||||
<a-button danger @click="handleDelete()"> |
||||
<DeleteOutlined /> |
||||
批量删除 |
||||
</a-button> |
||||
</Space> |
||||
</template> |
||||
|
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: 'i-ant-design:edit-outlined', |
||||
label: '修改', |
||||
onClick: () => openModal(true, record), |
||||
// auth: 'user_edit', |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:delete-outlined', |
||||
danger: true, |
||||
label: '删除', |
||||
// auth: 'user_delete', |
||||
popConfirm: { |
||||
title: '确定要删除数据吗?', |
||||
placement: 'left', |
||||
confirm: () => handleDelete(record.id), |
||||
}, |
||||
}, |
||||
|
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
|
||||
<SettingFormModal @register="registerModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -0,0 +1,54 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { Description } from '@/components/Description' |
||||
import type { DescItem } from '@/components/Description' |
||||
import type { SystemPost } from '@/api/system/post/types' |
||||
|
||||
defineOptions({ name: 'DetailModal' }) |
||||
|
||||
const baseDetail = ref({}) |
||||
const [registerModal] = useModalInner(async (data: SystemPost) => { |
||||
baseDetail.value = data |
||||
}) |
||||
|
||||
const baseSchema: DescItem[] = [ |
||||
{ |
||||
field: 'tenantName', |
||||
label: '所属租户', |
||||
span: 24, |
||||
}, |
||||
{ |
||||
field: 'categoryName', |
||||
label: '岗位类型', |
||||
}, |
||||
{ |
||||
field: 'postCode', |
||||
label: '岗位编号', |
||||
}, |
||||
{ |
||||
field: 'postName', |
||||
label: '岗位名称', |
||||
}, |
||||
{ |
||||
field: 'sort', |
||||
label: '岗位排序', |
||||
}, |
||||
{ |
||||
field: 'remark', |
||||
label: '岗位描述', |
||||
}, |
||||
] |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal |
||||
width="900px" |
||||
title="查看" |
||||
:show-cancel-btn="false" |
||||
:show-ok-btn="false" |
||||
@register="registerModal" |
||||
> |
||||
<Description :data="baseDetail" :schema="baseSchema" :column="2" /> |
||||
</BasicModal> |
||||
</template> |
@ -0,0 +1,57 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { getFormSchema } from './data' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { createPost, updatePost } from '@/api/system/post' |
||||
import type { SystemPost } from '@/api/system/post/types' |
||||
|
||||
defineOptions({ name: 'DictFormModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
const { t } = useI18n() |
||||
|
||||
const isUpdate = ref(false) |
||||
const [registerForm, { setFieldsValue, validate }] = useForm({ |
||||
name: 'post-form', |
||||
labelWidth: 100, |
||||
baseColProps: { span: 12 }, |
||||
schemas: getFormSchema(), |
||||
showActionButtonGroup: false, |
||||
actionColOptions: { span: 23 }, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: SystemPost) => { |
||||
isUpdate.value = true |
||||
setFieldsValue({ ...data }) |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate<SystemPost>() |
||||
setModalProps({ confirmLoading: true }) |
||||
await (isUpdate.value ? updatePost(values) : createPost(values)) |
||||
closeModal() |
||||
emit('success') |
||||
useMessage().createMessage.success(t('common.saveSuccessText')) |
||||
} |
||||
catch {} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal |
||||
width="55%" |
||||
:title="isUpdate ? t('action.edit') : t('action.create')" |
||||
:after-close="() => isUpdate = false" |
||||
@register="registerModal" |
||||
@ok="handleSubmit" |
||||
> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -0,0 +1,121 @@
|
||||
import { h } from 'vue' |
||||
import { Tag } from 'ant-design-vue' |
||||
import type { BasicColumn, FormSchema } from '@/components/Table' |
||||
import type { SystemPost } from '@/api/system/post/types' |
||||
import { getSystemDictionary } from '@/api/base/common' |
||||
import { getTenantId } from '@/utils/auth' |
||||
|
||||
export const columns: BasicColumn<SystemPost>[] = [ |
||||
{ |
||||
title: '所属租户', |
||||
dataIndex: 'tenantName', |
||||
}, |
||||
{ |
||||
title: '岗位类型', |
||||
dataIndex: 'categoryName', |
||||
customRender: ({ record }) => { |
||||
const { categoryName } = record |
||||
return h(Tag, { color: 'blue' }, () => categoryName) |
||||
}, |
||||
}, |
||||
{ |
||||
title: '岗位编号', |
||||
dataIndex: 'postCode', |
||||
}, |
||||
{ |
||||
title: '岗位名称', |
||||
dataIndex: 'postName', |
||||
}, |
||||
|
||||
{ |
||||
title: '岗位排序', |
||||
dataIndex: 'sort', |
||||
}, |
||||
|
||||
] |
||||
|
||||
export const searchFormSchema: FormSchema[] = [ |
||||
{ |
||||
label: '岗位类型', |
||||
field: 'category', |
||||
component: 'ApiSelect', |
||||
componentProps: { |
||||
api: () => getSystemDictionary('post_category'), |
||||
}, |
||||
colProps: { span: 6 }, |
||||
}, |
||||
{ |
||||
label: '岗位编号', |
||||
field: 'postCode', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
{ |
||||
label: '岗位名称', |
||||
field: 'postName', |
||||
component: 'Input', |
||||
colProps: { span: 6 }, |
||||
}, |
||||
] |
||||
|
||||
export function getFormSchema(): FormSchema[] { |
||||
return [ |
||||
{ |
||||
field: 'tenantId', |
||||
show: false, |
||||
component: 'Input', |
||||
defaultValue: getTenantId(), |
||||
}, |
||||
{ |
||||
field: 'id', |
||||
show: false, |
||||
component: 'Input', |
||||
}, |
||||
//
|
||||
{ |
||||
label: '岗位类型', |
||||
field: 'category', |
||||
required: true, |
||||
component: 'ApiSelect', |
||||
componentProps: { |
||||
api: async () => { |
||||
const res = await getSystemDictionary('post_category') |
||||
res.forEach((item) => { |
||||
item.dictKey = Number(item.dictKey) |
||||
}) |
||||
return res |
||||
}, |
||||
valueField: 'dictKey', |
||||
labelField: 'dictValue', |
||||
}, |
||||
|
||||
}, |
||||
{ |
||||
label: '岗位编号', |
||||
field: 'postCode', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '岗位名称', |
||||
field: 'postName', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
label: '岗位排序', |
||||
field: 'sort', |
||||
required: true, |
||||
component: 'InputNumber', |
||||
}, |
||||
{ |
||||
label: '岗位描述', |
||||
field: 'remark', |
||||
required: false, |
||||
component: 'InputTextArea', |
||||
colProps: { |
||||
span: 24, |
||||
}, |
||||
}, |
||||
] |
||||
} |
@ -0,0 +1,142 @@
|
||||
<script lang="ts" setup> |
||||
import { Space } from 'ant-design-vue' |
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue' |
||||
import { columns, searchFormSchema } from './data' |
||||
import PostFormModal from './PostFormModal.vue' |
||||
import DedailModal from './DedailModal.vue' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deletePost, getPostList } from '@/api/system/post' |
||||
import type { SystemPost } from '@/api/system/post/types' |
||||
import { getAllTenants } from '@/api/system/tenant' |
||||
|
||||
// import { usePermission } from '@/hooks/web/usePermission' |
||||
|
||||
defineOptions({ name: 'SystemPost' }) |
||||
|
||||
const { createMessage, createConfirm } = useMessage() |
||||
const [registerModal, { openModal }] = useModal<SystemPost>() |
||||
const [registerDetailModal, { openModal: openDetailModal }] = useModal<SystemPost>() |
||||
|
||||
// const { hasPermission } = usePermission() |
||||
|
||||
const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({ |
||||
async api(params) { |
||||
const res = await getAllTenants() |
||||
const list = await getPostList(params) |
||||
list.records = list.records.map((item) => { |
||||
const obj = res.find((tenant: SystemPost) => tenant.tenantId === item.tenantId) |
||||
return { |
||||
...item, |
||||
tenantName: obj?.tenantName, |
||||
} |
||||
}) |
||||
return list |
||||
}, |
||||
beforeFetch: (formData) => { |
||||
for (const key in formData) { |
||||
if (formData[key] === '') |
||||
formData[key] = undefined |
||||
} |
||||
return formData |
||||
}, |
||||
columns, |
||||
formConfig: { |
||||
labelWidth: 80, |
||||
schemas: searchFormSchema, |
||||
}, |
||||
rowKey: 'id', |
||||
rowSelection: {}, |
||||
useSearchForm: true, |
||||
bordered: true, |
||||
canResize: false, |
||||
actionColumn: { |
||||
width: 220, |
||||
title: '操作', |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
// auth: ['user_edit', 'user_delete'], |
||||
}, |
||||
}) |
||||
|
||||
async function handleDelete(id?: string) { |
||||
const ids = id ? [id] : (getSelectRowKeys() as string[]) |
||||
if (!ids.length) |
||||
return createMessage.warning('请选择要删除的数据') |
||||
|
||||
async function execute() { |
||||
try { |
||||
await deletePost(ids) |
||||
createMessage.success('删除成功') |
||||
reload() |
||||
clearSelectedRowKeys() |
||||
} |
||||
catch {} |
||||
} |
||||
|
||||
if (id) |
||||
return execute() |
||||
|
||||
createConfirm({ |
||||
iconType: 'warning', |
||||
title: '是否要删除选择的字典?', |
||||
onOk: execute, |
||||
}) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable :api="async () => ([] as SystemPost[])" @register="registerTable"> |
||||
<template #tableTitle> |
||||
<Space> |
||||
<a-button type="primary" @click="openModal"> |
||||
<PlusOutlined /> |
||||
新增 |
||||
</a-button> |
||||
<a-button danger @click="handleDelete()"> |
||||
<DeleteOutlined /> |
||||
批量删除 |
||||
</a-button> |
||||
</Space> |
||||
</template> |
||||
|
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: 'i-ant-design:eye-outlined', |
||||
label: '查看', |
||||
// auth: 'user_delete', |
||||
onClick: () => openDetailModal(true, record), |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:edit-outlined', |
||||
label: '修改', |
||||
onClick: () => openModal(true, record), |
||||
// auth: 'user_edit', |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:delete-outlined', |
||||
danger: true, |
||||
label: '删除', |
||||
// auth: 'user_delete', |
||||
popConfirm: { |
||||
title: '确定要删除数据吗?', |
||||
placement: 'left', |
||||
confirm: () => handleDelete(record.id), |
||||
}, |
||||
}, |
||||
|
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
|
||||
<PostFormModal @register="registerModal" @success="reload()" /> |
||||
<DedailModal @register="registerDetailModal" @success="reload()" /> |
||||
</div> |
||||
</template> |
@ -0,0 +1,38 @@
|
||||
import { computed, ref } from 'vue' |
||||
import { exportRegion } from '@/api/system/region' |
||||
import { useModal } from '@/components/Modal' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { noop } from '@/utils' |
||||
import { downloadByData } from '@/utils/file/download' |
||||
|
||||
export function useRegionActions() { |
||||
function handleExport() { |
||||
useMessage().createConfirm({ |
||||
title: '确定要导出行政区划数据吗?', |
||||
iconType: 'warning', |
||||
onOk() { |
||||
exportRegion() |
||||
.then((res) => { |
||||
downloadByData(res, '行政区划.xlsx') |
||||
}) |
||||
.catch(noop) |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
const [registerImportModal, { openModal: openImportModal }] = useModal() |
||||
const isCovered = ref(0) |
||||
const importTemplateUrl = '/baymax-system/region/export-template' |
||||
const importUrl = computed(() => { |
||||
return `/baymax-system/region/import-region?isCovered=${isCovered.value}` |
||||
}) |
||||
|
||||
return { |
||||
handleExport, |
||||
registerImportModal, |
||||
openImportModal, |
||||
importUrl, |
||||
importTemplateUrl, |
||||
isCovered, |
||||
} |
||||
} |
@ -0,0 +1,126 @@
|
||||
import { getSystemDictionary } from '@/api/base/common' |
||||
import { deleteRegion, updateRegion } from '@/api/system/region' |
||||
import type { Region } from '@/api/system/region/types' |
||||
import { useForm } from '@/components/Form' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { noop } from '@/utils' |
||||
|
||||
export function useRegionForm() { |
||||
const [registerForm, methods] = useForm({ |
||||
schemas: [ |
||||
{ |
||||
field: 'parentCode', |
||||
fields: [ |
||||
'id', |
||||
'parentId', |
||||
'code', |
||||
'ancestors', |
||||
'cityCode', |
||||
'cityName', |
||||
'districtCode', |
||||
'districtName', |
||||
'parentName', |
||||
'provinceCode', |
||||
'provinceName', |
||||
'townCode', |
||||
'townName', |
||||
'villageCode', |
||||
'villageName', |
||||
], |
||||
label: '父区划编号', |
||||
component: 'Input', |
||||
render({ values }) { |
||||
return values.parentCode |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'parentName', |
||||
label: '父区划名称', |
||||
component: 'Input', |
||||
render({ values }) { |
||||
return values.parentName |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'subCode', |
||||
label: '区划编号', |
||||
required: true, |
||||
component: 'Input', |
||||
componentProps({ formModel }) { |
||||
return { |
||||
addonBefore: formModel.parentCode, |
||||
} |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'name', |
||||
label: '区划名称', |
||||
required: true, |
||||
component: 'Input', |
||||
}, |
||||
{ |
||||
field: 'regionLevel', |
||||
label: '区划等级', |
||||
required: true, |
||||
component: 'ApiRadioGroup', |
||||
componentProps: { |
||||
api: () => getSystemDictionary('region'), |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'sort', |
||||
label: '区划排序', |
||||
required: true, |
||||
component: 'InputNumber', |
||||
}, |
||||
{ |
||||
field: 'remark', |
||||
label: '区划备注', |
||||
component: 'InputTextArea', |
||||
}, |
||||
], |
||||
labelWidth: 80, |
||||
baseColProps: { |
||||
span: 24, |
||||
style: { |
||||
textAlign: 'left', |
||||
}, |
||||
}, |
||||
actionColOptions: { |
||||
span: 24, |
||||
style: { |
||||
textAlign: 'center', |
||||
}, |
||||
}, |
||||
submitButtonOptions: { |
||||
text: '保存', |
||||
}, |
||||
}) |
||||
|
||||
function handleSubmit(data: Region) { |
||||
data = { ...data } |
||||
const TOP_CODE = '00' |
||||
data.parentCode = data.parentCode === TOP_CODE ? '' : data.parentCode |
||||
data.code = data.parentCode + data.subCode |
||||
updateRegion(data) |
||||
.then(() => { |
||||
useMessage().createMessage.success('保存成功') |
||||
}) |
||||
.catch(noop) |
||||
} |
||||
|
||||
function handleDelete(id: string) { |
||||
deleteRegion(id) |
||||
.then(() => { |
||||
useMessage().createMessage.success('删除成功') |
||||
}) |
||||
.catch(noop) |
||||
} |
||||
|
||||
return { |
||||
registerForm, |
||||
...methods, |
||||
handleSubmit, |
||||
handleDelete, |
||||
} |
||||
} |
@ -0,0 +1,39 @@
|
||||
import { ref } from 'vue' |
||||
import { lazyGetRegionList } from '@/api/system/region' |
||||
import type { Region } from '@/api/system/region/types' |
||||
import type { TreeItem } from '@/components/Tree' |
||||
|
||||
export function useRegionList() { |
||||
const regionList = ref<Region[]>([]) |
||||
async function lazyRegionList(node: TreeItem) { |
||||
return lazyGetRegionList(node.key.toString()) |
||||
.then((res) => { |
||||
return res.map((item) => { |
||||
return { |
||||
...item, |
||||
isLeaf: !item.hasChildren, |
||||
} |
||||
}) |
||||
}) |
||||
.catch(() => []) |
||||
} |
||||
|
||||
const TOP_KEY = '00' |
||||
|
||||
lazyRegionList({ key: TOP_KEY }) |
||||
.then(res => regionList.value = res) |
||||
|
||||
const selectedRegionKey = ref<string>() |
||||
|
||||
return { |
||||
lazyRegionList, |
||||
regionList, |
||||
selectedRegionKey, |
||||
setSelectedRegion(key?: string) { |
||||
selectedRegionKey.value = key |
||||
}, |
||||
reload() { |
||||
lazyRegionList({ key: TOP_KEY }) |
||||
}, |
||||
} |
||||
} |
@ -0,0 +1,165 @@
|
||||
<script setup lang="ts"> |
||||
import { DownloadOutlined, UploadOutlined } from '@ant-design/icons-vue' |
||||
import { Card, Empty, Popconfirm, Space, Switch } from 'ant-design-vue' |
||||
import { ref } from 'vue' |
||||
import { useRegionList } from './composables/useRegionList' |
||||
import { useRegionForm } from './composables/useRegionForm' |
||||
import { useRegionActions } from './composables/useRegionActions' |
||||
import { BasicTree } from '@/components/Tree' |
||||
import { BasicForm } from '@/components/Form' |
||||
import { getRegionDetail } from '@/api/system/region' |
||||
import { ImportModal } from '@/components/ImportModal' |
||||
import { noop } from '@/utils' |
||||
|
||||
const { |
||||
regionList, |
||||
selectedRegionKey, |
||||
lazyRegionList, |
||||
setSelectedRegion, |
||||
reload, |
||||
} = useRegionList() |
||||
|
||||
const { |
||||
registerForm, |
||||
setFieldsValue, |
||||
clearValidate, |
||||
handleSubmit, |
||||
handleDelete, |
||||
} = useRegionForm() |
||||
|
||||
const cardTitle = ref('') |
||||
function onSelectRegion(_, { nativeEvent, node }: { nativeEvent: PointerEvent, node: any }) { |
||||
// 点击 + Icon 时,nodeName 为 span,由此判断操作是否为添加 |
||||
const isCreateChild = (nativeEvent.target as HTMLElement).nodeName === 'SPAN' |
||||
cardTitle.value = isCreateChild ? '新增子节点' : '编辑节点' |
||||
const key = node.key |
||||
setSelectedRegion(key) |
||||
|
||||
if (!key) |
||||
return |
||||
|
||||
getRegionDetail(key) |
||||
.then((res) => { |
||||
let value |
||||
|
||||
if (isCreateChild) { |
||||
value = { |
||||
...res, |
||||
parentCode: res.code, |
||||
parentName: res.name, |
||||
code: undefined, |
||||
name: undefined, |
||||
sort: undefined, |
||||
remark: undefined, |
||||
subCode: undefined, |
||||
regionLevel: +res.regionLevel === 5 ? 5 : (+res.regionLevel + 1).toString(), |
||||
} |
||||
} |
||||
else { |
||||
value = { |
||||
...res, |
||||
subCode: res.code.replace(res.parentCode, ''), |
||||
regionLevel: res.regionLevel.toString(), |
||||
} |
||||
} |
||||
|
||||
setFieldsValue(value) |
||||
clearValidate() |
||||
}) |
||||
.catch(noop) |
||||
} |
||||
|
||||
const { |
||||
handleExport, |
||||
registerImportModal, |
||||
openImportModal, |
||||
importUrl, |
||||
importTemplateUrl, |
||||
isCovered, |
||||
} = useRegionActions() |
||||
</script> |
||||
|
||||
<template> |
||||
<div p="12px" flex="~ gap-12px"> |
||||
<Card min-w="20%"> |
||||
<div h="[calc(100vh-155px)]"> |
||||
<BasicTree |
||||
:selected-keys="selectedRegionKey ? [selectedRegionKey] : []" |
||||
search |
||||
:tree-data="regionList" |
||||
:load-data="lazyRegionList" |
||||
@select="onSelectRegion" |
||||
> |
||||
<template #headerAction> |
||||
<Space> |
||||
<a-button shape="circle" title="导入" @click="openImportModal"> |
||||
<UploadOutlined /> |
||||
</a-button> |
||||
<a-button shape="circle" title="导出" @click="handleExport"> |
||||
<DownloadOutlined /> |
||||
</a-button> |
||||
</Space> |
||||
</template> |
||||
|
||||
<template #title="{ title, hasChildren, key }"> |
||||
<div flex="~ items-center justify-between" py="8px" px="5px" box-border w-full> |
||||
<div flex="1" width="0" truncate :title="title"> |
||||
{{ title }} |
||||
</div> |
||||
<Space> |
||||
<span |
||||
class="i-ant-design:plus-outlined" |
||||
title="添加子节点" |
||||
data-create-child |
||||
/> |
||||
<Popconfirm |
||||
title="是否要删除数据?" |
||||
:class="[hasChildren ? 'text-gray-300 cursor-not-allowed' : '']" |
||||
:disabled="hasChildren" |
||||
@click.stop |
||||
@confirm="handleDelete(key)" |
||||
> |
||||
<span class="i-ant-design:delete-outlined" :title="hasChildren ? '存在子节点, 无法删除' : '删除数据'" /> |
||||
</Popconfirm> |
||||
</Space> |
||||
</div> |
||||
</template> |
||||
</BasicTree> |
||||
</div> |
||||
</Card> |
||||
<div w-0 flex-1> |
||||
<Card :title="cardTitle"> |
||||
<Empty v-if="!selectedRegionKey" description="请先在左侧选择一个节点" /> |
||||
<BasicForm v-else @register="registerForm" @submit="handleSubmit" /> |
||||
</Card> |
||||
</div> |
||||
|
||||
<ImportModal |
||||
:upload-url="importUrl" |
||||
:template-url="importTemplateUrl" |
||||
hint="请上传 .xls, .xlsx 标准格式文件" |
||||
@register="registerImportModal" |
||||
@success="reload" |
||||
> |
||||
<div> |
||||
<span mr="10px">数据覆盖</span> |
||||
<Switch v-model:checked="isCovered" :checked-value="1" :un-checked-value="0" /> |
||||
</div> |
||||
</ImportModal> |
||||
</div> |
||||
</template> |
||||
|
||||
<style scoped lang="less"> |
||||
:deep(.ant-tree-treenode) { |
||||
width: 100%; |
||||
height: 40px; |
||||
} |
||||
:deep(.ant-tree-node-content-wrapper) { |
||||
flex: 1; |
||||
width: 0; |
||||
height: 40px; |
||||
} |
||||
:deep(.ant-tree-switcher-icon) { |
||||
margin-top: 15px; |
||||
} |
||||
</style> |
@ -0,0 +1,50 @@
|
||||
<script lang="ts" setup> |
||||
import { authSettingSchema } from './data' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { BasicForm, useForm } from '@/components/Form' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { settingTenant } from '@/api/system/tenant' |
||||
import type { Tenant } from '@/api/system/tenant/types' |
||||
|
||||
defineOptions({ name: 'AuthSettingModal' }) |
||||
|
||||
const emit = defineEmits(['success', 'register']) |
||||
|
||||
const [registerForm, { setFieldsValue, validate }] = useForm({ |
||||
labelWidth: 120, |
||||
baseColProps: { span: 24 }, |
||||
schemas: authSettingSchema, |
||||
showActionButtonGroup: false, |
||||
}) |
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: Tenant) => { |
||||
setFieldsValue(data) |
||||
}) |
||||
|
||||
async function handleSubmit() { |
||||
try { |
||||
const values = await validate<Tenant>() |
||||
console.log(values) |
||||
|
||||
setModalProps({ confirmLoading: true }) |
||||
await settingTenant(values) |
||||
closeModal() |
||||
emit('success') |
||||
useMessage().createMessage.success('保存成功') |
||||
} |
||||
catch {} |
||||
finally { |
||||
setModalProps({ confirmLoading: false }) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal |
||||
title="租户授权配置" |
||||
@register="registerModal" |
||||
@ok="handleSubmit" |
||||
> |
||||
<BasicForm @register="registerForm" /> |
||||
</BasicModal> |
||||
</template> |
@ -0,0 +1,77 @@
|
||||
<script lang="ts" setup> |
||||
import { ref } from 'vue' |
||||
import { BasicModal, useModalInner } from '@/components/Modal' |
||||
import { Description } from '@/components/Description' |
||||
import type { DescItem } from '@/components/Description' |
||||
import type { Tenant } from '@/api/system/tenant/types' |
||||
|
||||
defineOptions({ name: 'DetailModal' }) |
||||
|
||||
const baseDetail = ref({}) |
||||
const [registerModal] = useModalInner(async (data: Tenant) => { |
||||
baseDetail.value = data |
||||
}) |
||||
|
||||
const baseSchema: DescItem[] = [ |
||||
{ |
||||
field: 'tenantId', |
||||
label: '租户ID', |
||||
span: 24, |
||||
}, |
||||
{ |
||||
field: 'tenantName', |
||||
label: '租户名称', |
||||
}, |
||||
{ |
||||
field: 'linkman', |
||||
label: '联系人', |
||||
}, |
||||
{ |
||||
field: 'contactNumber', |
||||
label: '联系电话', |
||||
render: (record) => { |
||||
return record || '-' |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'address', |
||||
label: '联系地址', |
||||
render: (record) => { |
||||
return record || '-' |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'accountNumber', |
||||
label: '账号额度', |
||||
render: (record) => { |
||||
return record || '-' |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'expireTime', |
||||
label: '过期时间', |
||||
render: (record) => { |
||||
return record || '无限制' |
||||
}, |
||||
}, |
||||
{ |
||||
field: 'domainUrl', |
||||
label: '绑定域名', |
||||
render: (record) => { |
||||
return record || '-' |
||||
}, |
||||
}, |
||||
] |
||||
</script> |
||||
|
||||
<template> |
||||
<BasicModal |
||||
width="900px" |
||||
title="查看" |
||||
:show-cancel-btn="false" |
||||
:show-ok-btn="false" |
||||
@register="registerModal" |
||||
> |
||||
<Description :data="baseDetail" :schema="baseSchema" :column="2" /> |
||||
</BasicModal> |
||||
</template> |
@ -0,0 +1,19 @@
|
||||
<script setup lang="ts"> |
||||
import { useAsyncState } from '@vueuse/core' |
||||
import { lazyGetDeptList } from '@/api/system/dept' |
||||
import { BasicTree } from '@/components/Tree' |
||||
|
||||
const emit = defineEmits(['select']) |
||||
|
||||
const { state: deptList } = useAsyncState(lazyGetDeptList, []) |
||||
|
||||
function onSelect(ids: string) { |
||||
emit('select', ids[0]) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div rounded="6px" px="10px" pt="12px" pb="6px" border-box bg="white dark:[var(--component-background)]" min-w="300px"> |
||||
<BasicTree :tree-data="deptList" search :field-names="{ title: 'deptName', key: 'id' }" @select="onSelect" /> |
||||
</div> |
||||
</template> |
@ -0,0 +1 @@
|
||||
export { default as DeptTree } from './DeptTree.vue' |
@ -0,0 +1,106 @@
|
||||
import { computed, ref } from 'vue' |
||||
import { deleteUser, exportUsers, resetPasswordByIds, unlockUserByIds } from '@/api/system/user' |
||||
import type { TableActionType } from '@/components/Table' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { downloadByData } from '@/utils/file/download' |
||||
import { useModal } from '@/components/Modal' |
||||
import { noop } from '@/utils' |
||||
|
||||
export function useUserActions(tableMethods: TableActionType) { |
||||
const { createMessage, createConfirm } = useMessage() |
||||
|
||||
function handleDelete(id?: string) { |
||||
const { getSelectRowKeys, clearSelectedRowKeys, reload } = tableMethods |
||||
const ids = id ? [id] : (getSelectRowKeys() as string[]) |
||||
|
||||
if (!ids.length) |
||||
return createMessage.warning('请先选择数据') |
||||
|
||||
async function execute() { |
||||
try { |
||||
await deleteUser(ids) |
||||
createMessage.success('删除成功') |
||||
await reload() |
||||
clearSelectedRowKeys() |
||||
} |
||||
catch {} |
||||
} |
||||
|
||||
if (id) |
||||
return execute() |
||||
|
||||
createConfirm({ |
||||
iconType: 'warning', |
||||
title: '是否要删除选择的用户?', |
||||
onOk: execute, |
||||
}) |
||||
} |
||||
|
||||
function resetPassword() { |
||||
const ids = tableMethods.getSelectRowKeys() as string[] |
||||
|
||||
if (!ids.length) |
||||
return createMessage.warning('请先选择数据') |
||||
|
||||
createConfirm({ |
||||
title: '确定将选择账号密码重置为 123456?', |
||||
iconType: 'warning', |
||||
onOk() { |
||||
resetPasswordByIds(ids) |
||||
.then(() => { |
||||
createMessage.success('重置成功') |
||||
}) |
||||
.catch(noop) |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
function unlockUser() { |
||||
const ids = tableMethods.getSelectRowKeys() as string[] |
||||
|
||||
if (!ids.length) |
||||
return createMessage.warning('请先选择数据') |
||||
|
||||
createConfirm({ |
||||
title: '确定将选择账号解封?', |
||||
iconType: 'warning', |
||||
onOk() { |
||||
unlockUserByIds(ids) |
||||
.then(() => { |
||||
createMessage.success('解封成功') |
||||
}) |
||||
.catch(noop) |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
function handleExportUsers() { |
||||
exportUsers() |
||||
.then((blob) => { |
||||
downloadByData(blob, '用户数据.xlsx') |
||||
}) |
||||
.catch(noop) |
||||
} |
||||
|
||||
const [registerImportModal, { openModal: openImportModal }] = useModal() |
||||
const isImportCovered = ref(0) |
||||
const importModalProps = computed(() => { |
||||
return { |
||||
title: '用户数据导入', |
||||
hint: '请上传 .xls,.xlsx 标准格式文件', |
||||
uploadUrl: `/baymax-system/user/import-user?isCovered=${isImportCovered.value}`, |
||||
templateUrl: '/baymax-system/user/export-template', |
||||
} |
||||
}) |
||||
|
||||
return { |
||||
handleDelete, |
||||
resetPassword, |
||||
unlockUser, |
||||
handleExportUsers, |
||||
registerImportModal, |
||||
openImportModal, |
||||
importModalProps, |
||||
isImportCovered, |
||||
} |
||||
} |
@ -1,92 +1,152 @@
|
||||
<script lang="ts" setup> |
||||
import { PlusOutlined } from '@ant-design/icons-vue' |
||||
import { Dropdown, Menu, Space, Switch } from 'ant-design-vue' |
||||
import { DeleteOutlined, DownOutlined, PlusOutlined } from '@ant-design/icons-vue' |
||||
import UserFormModal from './UserFormModal.vue' |
||||
import { columns, searchFormSchema } from './data' |
||||
import { DeptTree } from './components' |
||||
import { useUserActions } from './composables/useUserActions' |
||||
import { useI18n } from '@/hooks/web/useI18n' |
||||
import { useMessage } from '@/hooks/web/useMessage' |
||||
import { useModal } from '@/components/Modal' |
||||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
||||
import { deleteUser, getUserList } from '@/api/system/user' |
||||
import { getUserList } from '@/api/system/user' |
||||
import type { SystemUser } from '@/api/system/user/types' |
||||
import { usePermission } from '@/hooks/web/usePermission' |
||||
import { ImportModal } from '@/components/ImportModal' |
||||
|
||||
// import { usePermission } from '@/hooks/web/usePermission' |
||||
|
||||
defineOptions({ name: 'SystemUser' }) |
||||
|
||||
const { t } = useI18n() |
||||
const { createMessage } = useMessage() |
||||
const [registerModal, { openModal }] = useModal<SystemUser>() |
||||
|
||||
const { hasPermission } = usePermission() |
||||
// const { hasPermission } = usePermission() |
||||
|
||||
const [registerTable, { reload }] = useTable({ |
||||
const [registerTable, tableMethods] = useTable({ |
||||
api(params) { |
||||
return getUserList(params) |
||||
return getUserList({ |
||||
...params, |
||||
deptId: selectedDeptId, |
||||
}) |
||||
}, |
||||
rowKey: 'id', |
||||
columns, |
||||
formConfig: { |
||||
labelWidth: 80, |
||||
schemas: searchFormSchema, |
||||
autoSubmitOnEnter: true, |
||||
}, |
||||
useSearchForm: true, |
||||
bordered: true, |
||||
canResize: false, |
||||
rowSelection: {}, |
||||
actionColumn: { |
||||
width: 140, |
||||
title: t('common.action'), |
||||
dataIndex: 'action', |
||||
fixed: 'right', |
||||
auth: ['user_edit', 'user_delete'], |
||||
// auth: ['user_edit', 'user_delete'], |
||||
}, |
||||
}) |
||||
|
||||
async function handleDelete(id: string) { |
||||
try { |
||||
await deleteUser(id) |
||||
createMessage.success(t('common.delSuccessText')) |
||||
reload() |
||||
} |
||||
catch {} |
||||
let selectedDeptId: string |
||||
function onSelectedDept(id: string) { |
||||
selectedDeptId = id |
||||
tableMethods.setPagination({ current: 1 }) |
||||
tableMethods.reload() |
||||
} |
||||
|
||||
const { |
||||
handleDelete, |
||||
resetPassword, |
||||
unlockUser, |
||||
handleExportUsers, |
||||
registerImportModal, |
||||
importModalProps, |
||||
isImportCovered, |
||||
openImportModal, |
||||
} = useUserActions(tableMethods) |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<BasicTable :api="async () => ([] as SystemUser[])" @register="registerTable"> |
||||
<template v-if="hasPermission('user_add')" #tableTitle> |
||||
<a-button type="primary" @click="openModal"> |
||||
<PlusOutlined /> |
||||
{{ t('action.create') }} |
||||
</a-button> |
||||
</template> |
||||
<div flex="~"> |
||||
<DeptTree my="12px" ml="12px" @select="onSelectedDept" /> |
||||
<div flex="1" w-0> |
||||
<BasicTable :api="async () => ([] as SystemUser[])" @register="registerTable"> |
||||
<!-- v-if="hasPermission('user_add')" --> |
||||
<template #tableTitle> |
||||
<Space> |
||||
<a-button type="primary" @click="openModal"> |
||||
<PlusOutlined /> |
||||
{{ t('action.create') }} |
||||
</a-button> |
||||
<a-button danger @click="handleDelete()"> |
||||
<DeleteOutlined /> |
||||
批量删除 |
||||
</a-button> |
||||
<Dropdown> |
||||
<template #overlay> |
||||
<Menu> |
||||
<!-- <Menu.Item> |
||||
角色配置 |
||||
</Menu.Item> --> |
||||
<Menu.Item @click="resetPassword"> |
||||
密码重置 |
||||
</Menu.Item> |
||||
<!-- <Menu.Item> |
||||
平台配置 |
||||
</Menu.Item> --> |
||||
<Menu.Item @click="unlockUser"> |
||||
帐号解封 |
||||
</Menu.Item> |
||||
<Menu.Item @click="openImportModal()"> |
||||
数据导入 |
||||
</Menu.Item> |
||||
<Menu.Item @click="handleExportUsers"> |
||||
数据导出 |
||||
</Menu.Item> |
||||
</Menu> |
||||
</template> |
||||
<a-button> |
||||
更多操作 |
||||
<DownOutlined /> |
||||
</a-button> |
||||
</Dropdown> |
||||
</Space> |
||||
</template> |
||||
|
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: 'i-ant-design:edit-outlined', |
||||
label: t('action.edit'), |
||||
onClick: () => openModal(true, record), |
||||
auth: 'user_edit', |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:delete-outlined', |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
auth: 'user_delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: () => handleDelete(record.id), |
||||
<template #bodyCell="{ column, record }"> |
||||
<template v-if="column.key === 'action'"> |
||||
<TableAction |
||||
:actions="[ |
||||
{ |
||||
icon: 'i-ant-design:edit-outlined', |
||||
label: t('action.edit'), |
||||
onClick: () => openModal(true, record), |
||||
// auth: 'user_edit', |
||||
}, |
||||
{ |
||||
icon: 'i-ant-design:delete-outlined', |
||||
danger: true, |
||||
label: t('action.delete'), |
||||
// auth: 'user_delete', |
||||
popConfirm: { |
||||
title: t('common.delMessage'), |
||||
placement: 'left', |
||||
confirm: () => handleDelete(record.id), |
||||
}, |
||||
}, |
||||
}, |
||||
]" |
||||
/> |
||||
]" |
||||
/> |
||||
</template> |
||||
</template> |
||||
</template> |
||||
</BasicTable> |
||||
</BasicTable> |
||||
</div> |
||||
|
||||
<UserFormModal @register="registerModal" @success="reload()" /> |
||||
<UserFormModal @register="registerModal" @success="tableMethods.reload()" /> |
||||
<ImportModal v-bind="importModalProps" @register="registerImportModal"> |
||||
<div> |
||||
<span mr="10px">数据覆盖</span> |
||||
<Switch v-model:checked="isImportCovered" :checked-value="1" :un-checked-value="0" /> |
||||
</div> |
||||
</ImportModal> |
||||
</div> |
||||
</template> |
||||
|
Loading…
Reference in new issue