From 51c63c1b6948fd83cc626a4c93287e5ee47f74cf Mon Sep 17 00:00:00 2001 From: dap <15891557205@163.com> Date: Sat, 5 Oct 2024 12:12:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antd/src/adapter/vxe-table.ts | 25 +- .../src/views/monitor/operlog/data.tsx | 80 +- .../src/views/monitor/operlog/index.vue | 168 +- pnpm-lock.yaml | 4528 ++++++++--------- 4 files changed, 2373 insertions(+), 2428 deletions(-) diff --git a/apps/web-antd/src/adapter/vxe-table.ts b/apps/web-antd/src/adapter/vxe-table.ts index 969ec750..716d3c38 100644 --- a/apps/web-antd/src/adapter/vxe-table.ts +++ b/apps/web-antd/src/adapter/vxe-table.ts @@ -4,6 +4,8 @@ import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; import { Button, Image } from 'ant-design-vue'; +import { renderDict } from '#/utils/render'; + import { useVbenForm } from './form'; setupVbenVxeTable({ @@ -16,15 +18,15 @@ setupVbenVxeTable({ proxyConfig: { autoLoad: true, response: { - result: 'items', + result: 'rows', total: 'total', - list: 'items', + list: 'rows', }, showActiveMsg: true, showResponseMsg: false, }, round: true, - size: 'small', + size: 'medium', }, }); @@ -48,6 +50,23 @@ setupVbenVxeTable({ }, }); + /** + * 表格dict渲染 必传 props: { field: 参数名, dictName: 字典名 } + */ + vxeUI.renderer.add('DictTag', { + renderDefault(renderOpts, params) { + const { props } = renderOpts; + const field = props?.field; + const dictName = props?.dictName; + if (!field || !dictName) { + console.warn('DictTag: field or dictName is not provided'); + return 'error'; + } + const { row } = params; + return renderDict(row[field], dictName); + }, + }); + // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 // vxeUI.formats.add }, diff --git a/apps/web-antd/src/views/monitor/operlog/data.tsx b/apps/web-antd/src/views/monitor/operlog/data.tsx index 6e860ef7..8e2c7d9b 100644 --- a/apps/web-antd/src/views/monitor/operlog/data.tsx +++ b/apps/web-antd/src/views/monitor/operlog/data.tsx @@ -1,6 +1,4 @@ -import type { ColumnType } from 'ant-design-vue/es/table'; - -import type { FormSchemaGetter } from '#/adapter'; +import type { FormSchemaGetter, VxeGridProps } from '#/adapter'; import type { DescItem } from '#/components/description'; import { DictEnum } from '@vben/constants'; @@ -50,66 +48,48 @@ export const querySchema: FormSchemaGetter = () => [ component: 'RangePicker', fieldName: 'createTime', label: '操作时间', + componentProps: { + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, }, ]; -export const columns: ColumnType[] = [ +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { field: 'title', title: '系统模块' }, { - align: 'center', - dataIndex: 'title', - title: '系统模块', - }, - { - align: 'center', - customRender({ value }) { - return renderDict(value, DictEnum.SYS_OPER_TYPE); - }, - dataIndex: 'businessType', title: '操作类型', - }, - { - align: 'center', - dataIndex: 'operName', - title: '操作人员', - }, - { - align: 'center', - dataIndex: 'operIp', - title: 'IP地址', - }, - { - align: 'center', - dataIndex: 'operLocation', - title: 'IP信息', - }, - { - align: 'center', - customRender({ value }) { - return renderDict(value, DictEnum.SYS_COMMON_STATUS); + field: 'businessType', + cellRender: { + name: 'DictTag', + props: { field: 'businessType', dictName: DictEnum.SYS_OPER_TYPE }, }, - dataIndex: 'status', + }, + { field: 'operName', title: '操作人员' }, + { field: 'operIp', title: 'IP地址' }, + { field: 'operLocation', title: 'IP信息' }, + { + field: 'status', title: '操作状态', - }, - { - align: 'center', - dataIndex: 'operTime', - sorter: true, - title: '操作日期', - }, - { - align: 'center', - customRender({ text }) { - return `${text} ms`; + cellRender: { + name: 'DictTag', + props: { field: 'status', dictName: DictEnum.SYS_COMMON_STATUS }, }, - dataIndex: 'costTime', - sorter: true, + }, + { field: 'operTime', title: '操作日期' }, + { + field: 'costTime', title: '操作耗时', + formatter({ cellValue }) { + return `${cellValue} ms`; + }, }, { - align: 'center', - dataIndex: 'action', + field: 'action', fixed: 'right', + slots: { default: 'action' }, title: '操作', + width: 120, }, ]; diff --git a/apps/web-antd/src/views/monitor/operlog/index.vue b/apps/web-antd/src/views/monitor/operlog/index.vue index 05ef5f9e..39f27d9f 100644 --- a/apps/web-antd/src/views/monitor/operlog/index.vue +++ b/apps/web-antd/src/views/monitor/operlog/index.vue @@ -3,70 +3,160 @@ import type { Recordable } from '@vben/types'; import type { OperationLog } from '#/api/monitor/operlog/model'; -import { onMounted, ref } from 'vue'; +import { computed } from 'vue'; -import { Page, useVbenDrawer } from '@vben/common-ui'; +import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui'; import { $t } from '@vben/locales'; -import { Card, Table } from 'ant-design-vue'; +import { Modal, Space } from 'ant-design-vue'; +import dayjs from 'dayjs'; -import { useVbenForm } from '#/adapter'; -import { operLogList } from '#/api/monitor/operlog'; +import { useVbenVxeGrid, type VxeGridProps } from '#/adapter'; +import { + operLogClean, + operLogDelete, + operLogExport, + operLogList, +} from '#/api/monitor/operlog'; +import { downloadExcel } from '#/utils/file/download'; +import { confirmDeleteModal } from '#/utils/modal'; import { columns, querySchema } from './data'; import operationPreviewDrawer from './OperationPreviewDrawer.vue'; -const [QueryForm] = useVbenForm({ - // 默认展开 - collapsed: false, - // 所有表单项共用,可单独在表单内覆盖 - commonConfig: { - // 所有表单项 - componentProps: { - class: 'w-full', +const formOptions: VbenFormProps = { + schema: querySchema(), + wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4', +}; + +const gridOptions: VxeGridProps = { + checkboxConfig: { + // 高亮 + highlight: true, + // 翻页时保留选中状态 + reserve: true, + // 点击行选中 + trigger: 'row', + }, + columns, + height: 'auto', + keepSource: true, + pagerConfig: {}, + proxyConfig: { + ajax: { + query: async ({ page }, formValues) => { + // 区间选择器处理 + if (formValues?.createTime) { + formValues.params = { + beginTime: dayjs(formValues.createTime[0]).format( + 'YYYY-MM-DD 00:00:00', + ), + endTime: dayjs(formValues.createTime[1]).format( + 'YYYY-MM-DD 23:59:59', + ), + }; + Reflect.deleteProperty(formValues, 'createTime'); + } else { + Reflect.deleteProperty(formValues, 'params'); + } + return await operLogList({ + pageNum: page.currentPage, + pageSize: page.pageSize, + ...formValues, + }); + }, }, }, - schema: querySchema(), - // 是否可展开 - showCollapseButton: true, - submitButtonOptions: { - text: '查询', + rowConfig: { + isHover: true, + keyField: 'operId', }, - wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4', -}); + round: true, + align: 'center', + showOverflow: true, +}; -const dataSource = ref([]); - -onMounted(async () => { - const resp = await operLogList({ pageNum: 1, pageSize: 30 }); - dataSource.value = resp.rows; -}); +const [BasicTable, tableApi] = useVbenVxeGrid({ formOptions, gridOptions }); const [OperationPreviewDrawer, drawerApi] = useVbenDrawer({ connectedComponent: operationPreviewDrawer, }); +/** + * 预览 + * @param record 操作日志记录 + */ function handlePreview(record: Recordable) { drawerApi.setData({ record }); drawerApi.open(); } + +/** + * 清空全部日志 + */ +function handleClear() { + confirmDeleteModal({ + onValidated: async () => { + await operLogClean(); + }, + }); +} + +/** + * TODO: 选中条件的判断 + */ +const checked = computed(() => { + return true; +}); + +/** + * 删除日志 + */ +async function handleDelete() { + const rows = tableApi.grid.getCheckboxRecords(); + const ids = rows.map((row: any) => row.operId); + Modal.confirm({ + title: '提示', + okType: 'danger', + content: `确认删除选中的${ids.length}条操作日志吗?`, + onOk: async () => { + await operLogDelete(ids); + await tableApi.reload(); + }, + }); +}