From cdbb1a3117d3a3bb7e5ed54e2b6735d345ee7a20 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Mon, 21 Apr 2025 00:33:47 +0800 Subject: [PATCH] feat: add the admin dept view --- apps/web-antd/src/adapter/vxe-table.ts | 145 +++++++- apps/web-antd/src/api/api.ts | 2 +- apps/web-antd/src/api/auth.ts | 2 +- apps/web-antd/src/api/dept.ts | 71 ++++ apps/web-antd/src/api/index.ts | 1 + apps/web-antd/src/api/log.ts | 8 +- apps/web-antd/src/api/menu.ts | 6 +- apps/web-antd/src/api/monitor.ts | 4 +- apps/web-antd/src/api/role.ts | 2 +- apps/web-antd/src/views/admin/dept/index.vue | 331 ++++++++++++++++++- packages/icons/src/iconify/index.ts | 2 + 11 files changed, 556 insertions(+), 18 deletions(-) create mode 100644 apps/web-antd/src/api/dept.ts diff --git a/apps/web-antd/src/adapter/vxe-table.ts b/apps/web-antd/src/adapter/vxe-table.ts index d82acec20..2015dbf49 100644 --- a/apps/web-antd/src/adapter/vxe-table.ts +++ b/apps/web-antd/src/adapter/vxe-table.ts @@ -2,11 +2,13 @@ import type { Recordable } from '@vben/types'; import { h } from 'vue'; +import { IconifyIcon } from '@vben/icons'; +import { $te } from '@vben/locales'; import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; -import { get } from '@vben/utils'; +import { get, isFunction, isString } from '@vben/utils'; import { objectOmit } from '@vueuse/core'; -import { Button, Image, Switch, Tag } from 'ant-design-vue'; +import { Button, Image, Popconfirm, Switch, Tag } from 'ant-design-vue'; import { $t } from '#/locales'; @@ -31,7 +33,7 @@ setupVbenVxeTable({ response: { result: 'items', total: 'total', - list: 'items', + list: '', }, showActiveMsg: true, showResponseMsg: false, @@ -119,6 +121,143 @@ setupVbenVxeTable({ }, }); + /** + * 注册表格的操作按钮渲染器 + */ + vxeUI.renderer.add('CellOperation', { + renderTableDefault({ attrs, options, props }, { column, row }) { + const defaultProps = { size: 'small', type: 'link', ...props }; + let align = 'end'; + switch (column.align) { + case 'center': { + align = 'center'; + break; + } + case 'left': { + align = 'start'; + break; + } + default: { + align = 'end'; + break; + } + } + const presets: Recordable> = { + delete: { + danger: true, + text: $t('common.delete'), + }, + edit: { + text: $t('common.edit'), + }, + }; + const operations: Array> = ( + options || ['edit', 'delete'] + ) + .map((opt) => { + if (isString(opt)) { + return presets[opt] + ? { code: opt, ...presets[opt], ...defaultProps } + : { + code: opt, + text: $te(`common.${opt}`) ? $t(`common.${opt}`) : opt, + ...defaultProps, + }; + } else { + return { ...defaultProps, ...presets[opt.code], ...opt }; + } + }) + .map((opt) => { + const optBtn: Recordable = {}; + Object.keys(opt).forEach((key) => { + optBtn[key] = isFunction(opt[key]) ? opt[key](row) : opt[key]; + }); + return optBtn; + }) + .filter((opt) => opt.show !== false); + + function renderBtn(opt: Recordable, listen = true) { + return h( + Button, + { + ...props, + ...opt, + icon: undefined, + onClick: listen + ? () => + attrs?.onClick?.({ + code: opt.code, + row, + }) + : undefined, + }, + { + default: () => { + const content = []; + if (opt.icon) { + content.push( + h(IconifyIcon, { class: 'size-5', icon: opt.icon }), + ); + } + content.push(opt.text); + return content; + }, + }, + ); + } + + function renderConfirm(opt: Recordable) { + return h( + Popconfirm, + { + getPopupContainer(el) { + return ( + el + .closest('.vxe-table--viewport-wrapper') + ?.querySelector('.vxe-table--main-wrapper') + ?.querySelector('tbody') || document.body + ); + }, + placement: 'topLeft', + title: $t('ui.actionTitle.delete', [attrs?.nameTitle || '']), + ...props, + ...opt, + icon: undefined, + onConfirm: () => { + attrs?.onClick?.({ + code: opt.code, + row, + }); + }, + }, + { + default: () => renderBtn({ ...opt }, false), + description: () => + h( + 'div', + { class: 'truncate' }, + $t('ui.actionMessage.deleteConfirm', [ + row[attrs?.nameField || 'name'], + ]), + ), + }, + ); + } + + const btns = operations.map((opt) => + opt.code === 'delete' ? renderConfirm(opt) : renderBtn(opt), + ); + return h( + 'div', + { + class: 'flex table-operations', + style: { justifyContent: align }, + }, + btns, + ); + }, + }); + // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 // vxeUI.formats.add }, diff --git a/apps/web-antd/src/api/api.ts b/apps/web-antd/src/api/api.ts index 3ee51be32..a35c75c5d 100644 --- a/apps/web-antd/src/api/api.ts +++ b/apps/web-antd/src/api/api.ts @@ -21,6 +21,6 @@ export interface SysApiResult { /** * 获取系统 API 列表 */ -export function getSysApiList(params: SysApiParams) { +export async function getSysApiList(params: SysApiParams) { return requestClient.get('/api/v1/sys/apis', { params }); } diff --git a/apps/web-antd/src/api/auth.ts b/apps/web-antd/src/api/auth.ts index 9296712ab..723be42a1 100644 --- a/apps/web-antd/src/api/auth.ts +++ b/apps/web-antd/src/api/auth.ts @@ -24,7 +24,7 @@ export type RefreshTokenResult = LoginResult; /** * 登录验证码 */ -export function getCaptchaApi() { +export async function getCaptchaApi() { return requestClient.get('/api/v1/auth/captcha'); } diff --git a/apps/web-antd/src/api/dept.ts b/apps/web-antd/src/api/dept.ts new file mode 100644 index 000000000..dda143578 --- /dev/null +++ b/apps/web-antd/src/api/dept.ts @@ -0,0 +1,71 @@ +import { requestClient } from './request'; + +export interface SysDeptResult { + id: number; + name: string; + parent_id: number; + sort: number; + leader?: string; + phone?: string; + email?: string; + status: number; + created_time: string; +} + +export interface SysDeptTreeResult extends SysDeptResult { + children?: SysDeptTreeResult[]; +} + +export interface SysDeptParams { + name: string; + parent_id?: number; + sort?: number; + leader?: string; + phone?: string; + email?: string; + status: number; +} + +export interface SysDeptTreeParams { + name?: string; + leader?: string; + phone?: string; + status?: number; +} + +/** + * 获取部门树 + */ +export async function getSysDeptTree(params: SysDeptTreeParams) { + return requestClient.get('/api/v1/sys/depts', { + params, + }); +} + +/** + * 获取部门详情 + */ +export async function getSysDeptDetail(pk: number) { + return requestClient.get(`/api/v1/sys/depts/${pk}`); +} + +/** + * 创建部门 + */ +export async function createSysDept(data: SysDeptParams) { + return requestClient.post('/api/v1/sys/depts', data); +} + +/** + * 更新部门 + */ +export async function updateSysDept(pk: number, data: SysDeptParams) { + return requestClient.put(`/api/v1/sys/depts/${pk}`, data); +} + +/** + * 删除部门 + */ +export async function deleteSysDept(pk: number) { + return requestClient.delete(`/api/v1/sys/depts/${pk}`); +} diff --git a/apps/web-antd/src/api/index.ts b/apps/web-antd/src/api/index.ts index 02d6e7902..b6460fdd9 100644 --- a/apps/web-antd/src/api/index.ts +++ b/apps/web-antd/src/api/index.ts @@ -1,5 +1,6 @@ export * from './api'; export * from './auth'; +export * from './dept'; export * from './log'; export * from './menu'; export * from './monitor'; diff --git a/apps/web-antd/src/api/log.ts b/apps/web-antd/src/api/log.ts index aed47d0d0..2ee007fbb 100644 --- a/apps/web-antd/src/api/log.ts +++ b/apps/web-antd/src/api/log.ts @@ -13,7 +13,7 @@ export interface LoginLogParams { export interface LoginLogResult { id: number; username: string; - status: 0 | 1; + status: number; ip: string; country?: string; region?: string; @@ -39,17 +39,17 @@ export interface OperaLogResult { browser?: string; device?: string; args?: JSON; - status: 0 | 1; + status: number; code: string; msg: string; cost_time: number; opera_time: string; } -export function getLoginLogListApi(params: LoginLogParams) { +export async function getLoginLogListApi(params: LoginLogParams) { return requestClient.get('/api/v1/logs/login', { params }); } -export function getOperaLogListApi(params: OperaLogParams) { +export async function getOperaLogListApi(params: OperaLogParams) { return requestClient.get('/api/v1/logs/opera', { params }); } diff --git a/apps/web-antd/src/api/menu.ts b/apps/web-antd/src/api/menu.ts index 6d2bc9c80..ec28ee5a2 100644 --- a/apps/web-antd/src/api/menu.ts +++ b/apps/web-antd/src/api/menu.ts @@ -12,9 +12,9 @@ export interface SysMenuResult { type: number; component?: string; perms?: string; - status: 0 | 1; - display: 0 | 1; - cache: 0 | 1; + status: number; + display: number; + cache: number; remark?: string; parent_id?: number; created_time: string; diff --git a/apps/web-antd/src/api/monitor.ts b/apps/web-antd/src/api/monitor.ts index 2460f2a31..cb5f91e14 100644 --- a/apps/web-antd/src/api/monitor.ts +++ b/apps/web-antd/src/api/monitor.ts @@ -13,10 +13,10 @@ export interface RedisMonitorResult { stats: Record[]; } -export function getServerMonitor() { +export async function getServerMonitor() { return requestClient.get('/api/v1/monitors/server'); } -export function getRedisMonitor() { +export async function getRedisMonitor() { return requestClient.get('/api/v1/monitors/redis'); } diff --git a/apps/web-antd/src/api/role.ts b/apps/web-antd/src/api/role.ts index 1f09403f3..baad6eb4f 100644 --- a/apps/web-antd/src/api/role.ts +++ b/apps/web-antd/src/api/role.ts @@ -22,6 +22,6 @@ export interface SysRoleResult { /** * 获取系统角色列表 */ -export function getSysRoleList(params: SysRoleParams) { +export async function getSysRoleList(params: SysRoleParams) { return requestClient.get('/api/v1/sys/roles', { params }); } diff --git a/apps/web-antd/src/views/admin/dept/index.vue b/apps/web-antd/src/views/admin/dept/index.vue index 0102cc38c..2bbc3c443 100644 --- a/apps/web-antd/src/views/admin/dept/index.vue +++ b/apps/web-antd/src/views/admin/dept/index.vue @@ -1,5 +1,330 @@ + + diff --git a/packages/icons/src/iconify/index.ts b/packages/icons/src/iconify/index.ts index a0985ac15..b88c8b2b8 100644 --- a/packages/icons/src/iconify/index.ts +++ b/packages/icons/src/iconify/index.ts @@ -11,3 +11,5 @@ export const MdiGithub = createIconifyIcon('mdi:github'); export const MdiGoogle = createIconifyIcon('mdi:google'); export const MdiQqchat = createIconifyIcon('mdi:qqchat'); + +export const AddData = createIconifyIcon('material-symbols:add');