feat:完善 dept 部门 60%(新增、修改、删除)

pull/62/head
YunaiV 2025-03-29 15:10:08 +08:00
parent 32e0ce0325
commit d8f4e0a1aa
10 changed files with 736 additions and 91 deletions

View File

@ -0,0 +1,75 @@
import { requestClient } from '#/api/request';
export namespace SystemUserApi {
/** 用户信息 */
export interface SystemUser {
id?: number;
username: string;
nickname: string;
deptId: number;
postIds: string[];
email: string;
mobile: string;
sex: number;
avatar: string;
loginIp: string;
status: number;
remark: string;
createTime?: Date;
}
}
/** 查询用户管理列表 */
export function getUserPage(params: any) {
return requestClient.get('/system/user/page', { params });
}
/** 查询所有用户列表 */
export function getAllUser() {
return requestClient.get('/system/user/all');
}
/** 查询用户详情 */
export function getUser(id: number) {
return requestClient.get(`/system/user/get?id=${id}`);
}
/** 新增用户 */
export function createUser(data: SystemUserApi.SystemUser) {
return requestClient.post('/system/user/create', data);
}
/** 修改用户 */
export function updateUser(data: SystemUserApi.SystemUser) {
return requestClient.put('/system/user/update', data);
}
/** 删除用户 */
export function deleteUser(id: number) {
return requestClient.delete(`/system/user/delete?id=${id}`);
}
/** 导出用户 */
export function exportUser(params: any) {
return requestClient.download('/system/user/export', params);
}
/** 下载用户导入模板 */
export function importUserTemplate() {
return requestClient.download('/system/user/get-import-template');
}
/** 用户密码重置 */
export function resetUserPwd(id: number, password: string) {
return requestClient.put('/system/user/update-password', { id, password });
}
/** 用户状态修改 */
export function updateUserStatus(id: number, status: number) {
return requestClient.put('/system/user/update-status', { id, status });
}
/** 获取用户精简信息列表 */
export function getSimpleUserList(): Promise<SystemUserApi.SystemUser[]> {
return requestClient.get('/system/user/simple-list');
}

View File

@ -13,6 +13,7 @@ interface DictState {
dictCache: Dict;
}
// TODO @芋艿:可以共享么?
export const useDictStore = defineStore('dict', {
actions: {
getDictData(dictType: string, value: any) {

View File

@ -0,0 +1,466 @@
// todo @芋艿:要不要共享
/**
* Created by
*
*
*/
// ========== COMMON 模块 ==========
// 全局通用状态枚举
export const CommonStatusEnum = {
ENABLE: 0, // 开启
DISABLE: 1 // 禁用
}
// 全局用户类型枚举
export const UserTypeEnum = {
MEMBER: 1, // 会员
ADMIN: 2 // 管理员
}
// ========== SYSTEM 模块 ==========
/**
*
*/
export const SystemMenuTypeEnum = {
DIR: 1, // 目录
MENU: 2, // 菜单
BUTTON: 3 // 按钮
}
/**
*
*/
export const SystemRoleTypeEnum = {
SYSTEM: 1, // 内置角色
CUSTOM: 2 // 自定义角色
}
/**
*
*/
export const SystemDataScopeEnum = {
ALL: 1, // 全部数据权限
DEPT_CUSTOM: 2, // 指定部门数据权限
DEPT_ONLY: 3, // 部门数据权限
DEPT_AND_CHILD: 4, // 部门及以下数据权限
DEPT_SELF: 5 // 仅本人数据权限
}
/**
*
*/
export const SystemUserSocialTypeEnum = {
DINGTALK: {
title: '钉钉',
type: 20,
source: 'dingtalk',
img: 'https://s1.ax1x.com/2022/05/22/OzMDRs.png'
},
WECHAT_ENTERPRISE: {
title: '企业微信',
type: 30,
source: 'wechat_enterprise',
img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png'
}
}
// ========== INFRA 模块 ==========
/**
*
*/
export const InfraCodegenTemplateTypeEnum = {
CRUD: 1, // 基础 CRUD
TREE: 2, // 树形 CRUD
SUB: 3 // 主子表 CRUD
}
/**
*
*/
export const InfraJobStatusEnum = {
INIT: 0, // 初始化中
NORMAL: 1, // 运行中
STOP: 2 // 暂停运行
}
/**
* API
*/
export const InfraApiErrorLogProcessStatusEnum = {
INIT: 0, // 未处理
DONE: 1, // 已处理
IGNORE: 2 // 已忽略
}
// ========== PAY 模块 ==========
/**
*
*/
export const PayChannelEnum = {
WX_PUB: {
code: 'wx_pub',
name: '微信 JSAPI 支付'
},
WX_LITE: {
code: 'wx_lite',
name: '微信小程序支付'
},
WX_APP: {
code: 'wx_app',
name: '微信 APP 支付'
},
WX_NATIVE: {
code: 'wx_native',
name: '微信 Native 支付'
},
WX_WAP: {
code: 'wx_wap',
name: '微信 WAP 网站支付'
},
WX_BAR: {
code: 'wx_bar',
name: '微信条码支付'
},
ALIPAY_PC: {
code: 'alipay_pc',
name: '支付宝 PC 网站支付'
},
ALIPAY_WAP: {
code: 'alipay_wap',
name: '支付宝 WAP 网站支付'
},
ALIPAY_APP: {
code: 'alipay_app',
name: '支付宝 APP 支付'
},
ALIPAY_QR: {
code: 'alipay_qr',
name: '支付宝扫码支付'
},
ALIPAY_BAR: {
code: 'alipay_bar',
name: '支付宝条码支付'
},
WALLET: {
code: 'wallet',
name: '钱包支付'
},
MOCK: {
code: 'mock',
name: '模拟支付'
}
}
/**
*
*/
export const PayDisplayModeEnum = {
URL: {
mode: 'url'
},
IFRAME: {
mode: 'iframe'
},
FORM: {
mode: 'form'
},
QR_CODE: {
mode: 'qr_code'
},
APP: {
mode: 'app'
}
}
/**
*
*/
export const PayType = {
WECHAT: 'WECHAT',
ALIPAY: 'ALIPAY',
MOCK: 'MOCK'
}
/**
*
*/
export const PayOrderStatusEnum = {
WAITING: {
status: 0,
name: '未支付'
},
SUCCESS: {
status: 10,
name: '已支付'
},
CLOSED: {
status: 20,
name: '未支付'
}
}
// ========== MALL - 商品模块 ==========
/**
* SPU
*/
export const ProductSpuStatusEnum = {
RECYCLE: {
status: -1,
name: '回收站'
},
DISABLE: {
status: 0,
name: '下架'
},
ENABLE: {
status: 1,
name: '上架'
}
}
// ========== MALL - 营销模块 ==========
/**
*
*/
export const CouponTemplateValidityTypeEnum = {
DATE: {
type: 1,
name: '固定日期可用'
},
TERM: {
type: 2,
name: '领取之后可用'
}
}
/**
*
*/
export const CouponTemplateTakeTypeEnum = {
USER: {
type: 1,
name: '直接领取'
},
ADMIN: {
type: 2,
name: '指定发放'
},
REGISTER: {
type: 3,
name: '新人券'
}
}
/**
*
*/
export const PromotionProductScopeEnum = {
ALL: {
scope: 1,
name: '通用劵'
},
SPU: {
scope: 2,
name: '商品劵'
},
CATEGORY: {
scope: 3,
name: '品类劵'
}
}
/**
*
*/
export const PromotionConditionTypeEnum = {
PRICE: {
type: 10,
name: '满 N 元'
},
COUNT: {
type: 20,
name: '满 N 件'
}
}
/**
*
*/
export const PromotionDiscountTypeEnum = {
PRICE: {
type: 1,
name: '满减'
},
PERCENT: {
type: 2,
name: '折扣'
}
}
// ========== MALL - 交易模块 ==========
/**
*
*/
export const BrokerageBindModeEnum = {
ANYTIME: {
mode: 1,
name: '首次绑定'
},
REGISTER: {
mode: 2,
name: '注册绑定'
},
OVERRIDE: {
mode: 3,
name: '覆盖绑定'
}
}
/**
*
*/
export const BrokerageEnabledConditionEnum = {
ALL: {
condition: 1,
name: '人人分销'
},
ADMIN: {
condition: 2,
name: '指定分销'
}
}
/**
*
*/
export const BrokerageRecordBizTypeEnum = {
ORDER: {
type: 1,
name: '获得推广佣金'
},
WITHDRAW: {
type: 2,
name: '提现申请'
}
}
/**
*
*/
export const BrokerageWithdrawStatusEnum = {
AUDITING: {
status: 0,
name: '审核中'
},
AUDIT_SUCCESS: {
status: 10,
name: '审核通过'
},
AUDIT_FAIL: {
status: 20,
name: '审核不通过'
},
WITHDRAW_SUCCESS: {
status: 11,
name: '提现成功'
},
WITHDRAW_FAIL: {
status: 21,
name: '提现失败'
}
}
/**
*
*/
export const BrokerageWithdrawTypeEnum = {
WALLET: {
type: 1,
name: '钱包'
},
BANK: {
type: 2,
name: '银行卡'
},
WECHAT: {
type: 3,
name: '微信'
},
ALIPAY: {
type: 4,
name: '支付宝'
}
}
/**
*
*/
export const DeliveryTypeEnum = {
EXPRESS: {
type: 1,
name: '快递发货'
},
PICK_UP: {
type: 2,
name: '到店自提'
}
}
/**
* -
*/
export const TradeOrderStatusEnum = {
UNPAID: {
status: 0,
name: '待支付'
},
UNDELIVERED: {
status: 10,
name: '待发货'
},
DELIVERED: {
status: 20,
name: '已发货'
},
COMPLETED: {
status: 30,
name: '已完成'
},
CANCELED: {
status: 40,
name: '已取消'
}
}
// ========== ERP - 企业资源计划 ==========
export const ErpBizType = {
PURCHASE_ORDER: 10,
PURCHASE_IN: 11,
PURCHASE_RETURN: 12,
SALE_ORDER: 20,
SALE_OUT: 21,
SALE_RETURN: 22
}
// ========== BPM 模块 ==========
export const BpmModelType = {
BPMN: 10, // BPMN 设计器
SIMPLE: 20 // 简易设计器
}
export const BpmModelFormType = {
NORMAL: 10, // 流程表单
CUSTOM: 20 // 业务表单
}
export const BpmProcessInstanceStatus = {
NOT_START: -1, // 未开始
RUNNING: 1, // 审批中
APPROVE: 2, // 审批通过
REJECT: 3, // 审批不通过
CANCEL: 4 // 已取消
}
export const BpmAutoApproveType = {
NONE: 0, // 不自动通过
APPROVE_ALL: 1, // 仅审批一次,后续重复的审批节点均自动通过
APPROVE_SEQUENT: 2, // 仅针对连续审批的节点自动通过
}

View File

@ -1,5 +1,6 @@
import type { DefaultOptionType } from 'ant-design-vue/es/select';
// TODO @芋艿:后续再优化
// TODO @芋艿:可以共享么?
import { isObject } from '@vben/utils';

View File

@ -0,0 +1,56 @@
// TODO @芋艿1代码优化2是不是抽到公共的
/**
*
* @param {*} data
* @param {*} id id 'id'
* @param {*} parentId 'parentId'
* @param {*} children 'children'
*/
export const handleTree = (data: any[], id?: string, parentId?: string, children?: string) => {
if (!Array.isArray(data)) {
console.warn('data must be an array')
return []
}
const config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
}
const childrenListMap = {}
const nodeIds = {}
const tree: any[] = []
for (const d of data) {
const parentId = d[config.parentId]
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = []
}
nodeIds[d[config.id]] = d
childrenListMap[parentId].push(d)
}
for (const d of data) {
const parentId = d[config.parentId]
if (nodeIds[parentId] == null) {
tree.push(d)
}
}
for (const t of tree) {
adaptToChildrenList(t)
}
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]]
}
if (o[config.childrenList]) {
for (const c of o[config.childrenList]) {
adaptToChildrenList(c)
}
}
}
return tree
}

View File

@ -3,67 +3,125 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn } from '#/adapter/vxe-table';
import type { SystemDeptApi } from '#/api/system/dept';
import { $t } from '#/locales';
import { z } from '#/adapter/form';
import { getDeptList } from '#/api/system/dept';
import { $t } from '#/locales';
import { DICT_TYPE } from '#/utils/dict';
import { getSimpleUserList } from '#/api/system/user';
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
import { CommonStatusEnum } from '#/utils/constants';
import { handleTree } from '#/utils/tree';
/** 获取编辑表单的字段配置 */
// TODO @芋艿:表单的整理
export function useSchema(): VbenFormSchema[] {
return [
{
component: 'ApiTreeSelect',
componentProps: {
allowClear: true,
api: getDeptList,
api: async () => {
const data = await getDeptList();
data.unshift({
id: 0,
name: '顶级部门',
});
return handleTree(data);
},
class: 'w-full',
labelField: 'name',
valueField: 'id',
childrenField: 'children'
childrenField: 'children',
placeholder: '请选择上级部门',
treeDefaultExpandAll: true,
},
fieldName: 'parentId',
label: '上级部门',
// TODO @芋艿number 的必填,写起来有点麻烦,后续得研究下;
rules: z
.number()
.nullable()
.refine((val) => val != null && val >= 0, '上级部门不能为空')
.default(null),
},
{
component: 'Input',
componentProps: {
placeholder: '请输入部门名称',
},
fieldName: 'name',
label: $t('system.dept.deptName'),
label: '部门名称',
rules: z
.string()
.min(2, $t('ui.formRules.minLength', [$t('system.dept.deptName'), 2]))
.max(
20,
$t('ui.formRules.maxLength', [$t('system.dept.deptName'), 20]),
),
.min(2, $t('ui.formRules.minLength', ['部门名称', 2]))
.max(20, $t('ui.formRules.maxLength', ['部门名称', 20])),
},
{
component: 'InputNumber',
componentProps: {
min: 0,
class: 'w-full',
controlsPosition: 'right',
placeholder: '请输入显示排序',
},
fieldName: 'sort',
label: '显示排序',
rules: z
.number()
.nullable()
.refine((val) => val != null && val >= 0, '显示排序不能为空')
.default(null),
},
{
component: 'ApiSelect',
componentProps: {
api: getSimpleUserList,
class: 'w-full',
labelField: 'nickname',
valueField: 'id',
placeholder: '请选择负责人',
allowClear: true,
},
fieldName: 'leaderUserId',
label: '负责人',
rules: z.number().optional(),
},
{
component: 'Input',
componentProps: {
maxLength: 11,
placeholder: '请输入联系电话',
},
fieldName: 'phone',
label: '联系电话',
rules: z
.string()
// TODO @芋艿:未来怎么拓展一个手机的
.regex(/^1[3|4|5|6|7|8|9][0-9]\d{8}$/, '请输入正确的手机号码')
.optional(),
},
{
component: 'Input',
componentProps: {
maxLength: 50,
placeholder: '请输入邮箱',
},
fieldName: 'email',
label: '邮箱',
rules: z
.string()
.email('请输入正确的邮箱地址')
.max(50, $t('ui.formRules.maxLength', ['邮箱', 50]))
.optional(),
},
{
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
options: [
{ label: $t('common.enabled'), value: 1 },
{ label: $t('common.disabled'), value: 0 },
],
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
optionType: 'button',
},
defaultValue: 1,
fieldName: 'status',
label: $t('system.dept.status'),
},
{
component: 'Textarea',
componentProps: {
maxLength: 50,
rows: 3,
showCount: true,
},
fieldName: 'remark',
label: $t('system.dept.remark'),
rules: z
.string()
.max(50, $t('ui.formRules.maxLength', [$t('system.dept.remark'), 50]))
.optional(),
label: '状态',
rules: z.number().default(CommonStatusEnum.ENABLE),
},
];
}
@ -93,7 +151,10 @@ export function useColumns(
minWidth: 100,
},
{
cellRender: { name: 'CellDict', props: { type: DICT_TYPE.COMMON_STATUS } },
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.COMMON_STATUS },
},
field: 'status',
title: '状态',
minWidth: 100,

View File

@ -1,5 +1,8 @@
<script lang="ts" setup>
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { SystemDeptApi } from '#/api/system/dept';
import { $t } from '#/locales';
@ -25,7 +28,7 @@ function onEdit(row: SystemDeptApi.SystemDept) {
/** 添加下级部门 */
function onAppend(row: SystemDeptApi.SystemDept) {
formModalApi.setData({ pid: row.id }).open();
formModalApi.setData({ parentId: row.id }).open();
}
/** 创建新部门 */
@ -34,24 +37,22 @@ function onCreate() {
}
/** 删除部门 */
function onDelete(row: SystemDeptApi.SystemDept) {
async function onDelete(row: SystemDeptApi.SystemDept) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
});
// TODO @ await
deleteDept(row.id as number)
.then(() => {
try {
await deleteDept(row.id as number);
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_process_msg',
});
refreshGrid();
})
.catch(() => {
} catch (error) {
hideLoading();
});
}
}
/** 表格操作按钮的回调函数 */

View File

@ -7,7 +7,7 @@ import { useVbenModal } from '@vben/common-ui';
import { Button } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { createDept, updateDept } from '#/api/system/dept';
import { createDept, updateDept, getDept } from '#/api/system/dept';
import { $t } from '#/locales';
import { useSchema } from '../data';
@ -21,7 +21,7 @@ const getTitle = computed(() => {
});
const [Form, formApi] = useVbenForm({
layout: 'vertical',
layout: 'horizontal',
schema: useSchema(),
showDefaultActions: false,
});
@ -31,37 +31,42 @@ function resetForm() {
formApi.setValues(formData.value || {});
}
// TODO @
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
const { valid } = await formApi.validate();
if (valid) {
if (!valid) {
return;
}
modalApi.lock();
const data = formApi.getValues();
const data = (await formApi.getValues()) as SystemDeptApi.SystemDept;
try {
await (formData.value?.id
? updateDept(formData.value.id, data)
? updateDept({ id: formData.value.id, ...data })
: createDept(data));
modalApi.close();
await modalApi.close();
emit('success');
} finally {
modalApi.lock(false);
}
}
},
onOpenChange(isOpen) {
// TODO @
if (isOpen) {
const data = modalApi.getData<SystemDeptApi.SystemDept>();
if (data) {
// TODO @
if (data.parentId === 0) {
data.parentId = undefined;
async onOpenChange(isOpen) {
if (!isOpen) {
return;
}
let data = modalApi.getData<SystemDeptApi.SystemDept>();
if (!data) {
return;
}
if (data.id) {
modalApi.lock();
try {
data = await getDept(data.id);
} finally {
modalApi.lock(false);
}
}
formData.value = data;
formApi.setValues(formData.value);
}
}
await formApi.setValues(formData.value);
},
});
</script>

View File

@ -1,15 +1,5 @@
{
"title": "System Management",
"dept": {
"name": "Department",
"title": "Department Management",
"deptName": "Department Name",
"status": "Status",
"createTime": "Create Time",
"remark": "Remark",
"operation": "Operation",
"parentDept": "Parent Department"
},
"menu": {
"title": "Menu Management",
"parent": "Parent Menu",

View File

@ -1,15 +1,4 @@
{
"dept": {
"list": "部门列表",
"createTime": "创建时间",
"deptName": "部门名称",
"name": "部门",
"operation": "操作",
"parentDept": "上级部门",
"remark": "备注",
"status": "状态",
"title": "部门管理"
},
"menu": {
"list": "菜单列表",
"activeIcon": "激活图标",