feat: 部门管理

This commit is contained in:
dap 2024-09-23 11:33:55 +08:00
parent 03ea227f1f
commit 719ebbcf37
5 changed files with 359 additions and 4 deletions

View File

@ -0,0 +1,45 @@
import type { Dept } from './model';
import type { ID, PageQuery } from '#/api/common';
import { requestClient } from '#/api/request';
enum Api {
deptList = '/system/dept/list',
deptNodeInfo = '/system/dept/list/exclude',
root = '/system/dept',
}
export function deptList(params?: PageQuery) {
return requestClient.get<Dept[]>(Api.deptList, { params });
}
/**
*
* @param deptId ID
* @returns void
*/
export function deptNodeList(deptId: ID) {
return requestClient.get<Dept[]>(`${Api.deptNodeInfo}/${deptId}`);
}
export function deptInfo(deptId: ID) {
return requestClient.get<Dept>(`${Api.root}/${deptId}`);
}
export function deptAdd(data: any) {
return requestClient.postWithMsg<void>(Api.root, data);
}
export function deptUpdate(data: any) {
return requestClient.putWithMsg<void>(Api.root, data);
}
/**
*
* @param deptId ID
* @returns void
*/
export function deptRemove(deptId: ID) {
return requestClient.deleteWithMsg<void>(`${Api.root}/${deptId}`);
}

View File

@ -0,0 +1,19 @@
export interface Dept {
createBy: string;
createTime: string;
updateBy?: string;
updateTime?: string;
remark?: string;
deptId: number;
parentId: number;
ancestors: string;
deptName: string;
orderNum: number;
leader: string;
phone: string;
email: string;
status: string;
delFlag: string;
parentName?: string;
children?: Dept[];
}

View File

@ -0,0 +1,99 @@
import { DictEnum } from '@vben/constants';
import { getPopupContainer } from '@vben/utils';
import { type FormSchemaGetter, z } from '#/adapter';
import { getDictOptions } from '#/utils/dict';
export const drawerSchema: FormSchemaGetter = () => [
{
component: 'Input',
dependencies: {
show: () => false,
triggerFields: [''],
},
fieldName: 'deptId',
},
{
component: 'TreeSelect',
componentProps: {
getPopupContainer,
placeholder: '请选择',
},
dependencies: {
show: (model) => model.parentId !== 0,
triggerFields: ['parentId'],
},
fieldName: 'parentId',
label: '上级部门',
rules: 'selectRequired',
},
{
component: 'Input',
componentProps: {
placeholder: '请输入',
},
fieldName: 'deptName',
label: '部门名称',
rules: 'required',
},
{
component: 'InputNumber',
componentProps: {
placeholder: '请输入',
},
fieldName: 'orderNum',
label: '显示排序',
rules: 'required',
},
{
component: 'Input',
componentProps: {
placeholder: '请输入',
},
fieldName: 'deptCategory',
label: '类别编码',
},
{
component: 'Select',
componentProps: {
// 选中了就只能修改 不能重置为无负责人
allowClear: false,
getPopupContainer,
placeholder: '请选择',
},
fieldName: 'leader',
label: '负责人',
},
{
component: 'Input',
componentProps: {
placeholder: '请输入',
},
fieldName: 'phone',
label: '联系电话',
rules: z
.string()
.regex(/^\d{1,3}-\d{8,11}$/, { message: '请输入正确的手机号' })
.optional(),
},
{
component: 'Input',
componentProps: {
placeholder: '请输入',
},
fieldName: 'email',
label: '邮箱',
rules: z.string().email({ message: '请输入正确的邮箱' }).optional(),
},
{
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE),
optionType: 'button',
},
defaultValue: '0',
fieldName: 'status',
label: '状态',
},
];

View File

@ -0,0 +1,166 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { addFullName, listToTree } from '@vben/utils';
import { useVbenForm } from '#/adapter';
import {
deptAdd,
deptInfo,
deptList,
deptNodeList,
deptUpdate,
} from '#/api/system/dept';
import { listUserByDeptId } from '#/api/system/user';
import { drawerSchema } from './data';
const emit = defineEmits<{ reload: [] }>();
interface DrawerProps {
id?: number | string;
update: boolean;
}
const isUpdate = ref(false);
const title = computed(() => {
return isUpdate.value ? $t('pages.common.edit') : $t('pages.common.add');
});
const [BasicForm, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-2',
labelWidth: 80,
},
schema: drawerSchema(),
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
async function getDeptTree(deptId?: number | string, exclude = false) {
let ret: any[] = [];
ret = await (!deptId || exclude ? deptList({}) : deptNodeList(deptId));
const treeData = listToTree(ret, { id: 'deptId', pid: 'parentId' });
// xx-xx-xx
addFullName(treeData, 'deptName', ' / ');
return treeData;
}
async function initDeptSelect(deptId?: number | string) {
// TreeSelect
const treeData = await getDeptTree(deptId, !isUpdate.value);
formApi.updateSchema([
{
componentProps: {
fieldNames: { label: 'deptName', value: 'deptId' },
showSearch: true,
treeData,
treeDefaultExpandAll: true,
treeLine: { showLeafIcon: false },
//
treeNodeLabelProp: 'fullName',
},
fieldName: 'parentId',
},
]);
}
/**
* 部门管理员下拉框 更新时才会enable
* @param deptId
*/
async function initDeptUsers(deptId: number | string) {
const ret = await listUserByDeptId(deptId);
const options = ret.map((user) => ({
label: `${user.userName} | ${user.nickName}`,
value: user.userId,
}));
formApi.updateSchema([
{
componentProps: {
disabled: ret.length === 0,
options,
placeholder: ret.length === 0 ? '该部门暂无用户' : '请选择部门负责人',
},
fieldName: 'leader',
},
]);
}
async function setLeaderOptions() {
formApi.updateSchema([
{
componentProps: {
disabled: true,
options: [],
placeholder: '仅在更新时可选部门负责人',
},
fieldName: 'leader',
},
]);
}
const [BasicDrawer, drawerApi] = useVbenDrawer({
onCancel: handleCancel,
onConfirm: handleConfirm,
async onOpenChange(isOpen) {
if (!isOpen) {
return null;
}
drawerApi.drawerLoading(true);
const { id, update } = drawerApi.getData() as DrawerProps;
isUpdate.value = update;
if (id) {
await formApi.setFieldValue('parentId', id);
if (update) {
const ret = await deptInfo(id);
Object.keys(ret).forEach((key) => {
formApi.setFieldValue(key, ret[key as keyof typeof ret]);
});
}
}
await (update && id ? initDeptUsers(id) : setLeaderOptions());
/** 部门选择 下拉框 */
await initDeptSelect(id);
drawerApi.drawerLoading(false);
},
});
async function handleConfirm() {
try {
drawerApi.drawerLoading(true);
const { valid } = await formApi.validate();
if (!valid) {
return;
}
const data = await formApi.getValues();
await (isUpdate.value ? deptUpdate(data) : deptAdd(data));
emit('reload');
await handleCancel();
} catch (error) {
console.error(error);
} finally {
drawerApi.drawerLoading(false);
}
}
async function handleCancel() {
drawerApi.close();
await formApi.resetForm();
}
</script>
<template>
<BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
<BasicForm />
</BasicDrawer>
</template>

View File

@ -1,9 +1,35 @@
<script setup lang="ts">
import CommonSkeleton from '#/views/common';
import { Page, useVbenDrawer } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { Space } from 'ant-design-vue';
import deptDrawer from './dept-drawer.vue';
const [DeptDrawer, drawerApi] = useVbenDrawer({
connectedComponent: deptDrawer,
});
function handleAdd() {
drawerApi.setData({});
drawerApi.open();
}
function handleTest(id: number | string) {
drawerApi.setData({ id });
drawerApi.open();
}
</script>
<template>
<div>
<CommonSkeleton />
</div>
<Page>
<Space>
<a-button type="primary" @click="handleAdd">
{{ $t('pages.common.add') }}
</a-button>
<a-button @click="handleTest(103)"> 新增 上级id=103 </a-button>
<a-button @click="handleTest(105)"> 新增 上级id=105 </a-button>
</Space>
<DeptDrawer />
</Page>
</template>