物业代码生成

This commit is contained in:
2025-06-18 11:03:42 +08:00
commit 1262d4c745
1881 changed files with 249599 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
import type { FormSchemaGetter } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import { DictEnum } from '@vben/constants';
import { getDictOptions } from '#/utils/dict';
import { renderDict } from '#/utils/render';
export const querySchema: FormSchemaGetter = () => [
{
component: 'Input',
fieldName: 'title',
label: '系统模块',
},
{
component: 'Input',
fieldName: 'operName',
label: '操作人员',
},
{
component: 'Select',
componentProps: {
options: getDictOptions(DictEnum.SYS_OPER_TYPE),
},
fieldName: 'businessType',
label: '操作类型',
},
{
component: 'Input',
fieldName: 'operIp',
label: '操作IP',
},
{
component: 'Select',
componentProps: {
options: getDictOptions(DictEnum.SYS_COMMON_STATUS),
},
fieldName: 'status',
label: '状态',
},
{
component: 'RangePicker',
fieldName: 'createTime',
label: '操作时间',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
},
},
];
export const columns: VxeGridProps['columns'] = [
{ type: 'checkbox', width: 60 },
{ field: 'title', title: '系统模块' },
{
title: '操作类型',
field: 'businessType',
slots: {
default: ({ row }) => {
return renderDict(row.businessType, DictEnum.SYS_OPER_TYPE);
},
},
},
{ field: 'operName', title: '操作人员' },
{ field: 'operIp', title: 'IP地址' },
{ field: 'operLocation', title: 'IP信息' },
{
field: 'status',
title: '操作状态',
slots: {
default: ({ row }) => {
return renderDict(row.status, DictEnum.SYS_COMMON_STATUS);
},
},
},
{ field: 'operTime', title: '操作日期', sortable: true },
{
field: 'costTime',
title: '操作耗时',
sortable: true,
formatter({ cellValue }) {
return `${cellValue} ms`;
},
},
{
field: 'action',
fixed: 'right',
slots: { default: 'action' },
title: '操作',
resizable: false,
width: 'auto',
},
];

View File

@@ -0,0 +1,184 @@
<script setup lang="ts">
import type { VbenFormProps } from '@vben/common-ui';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { PageQuery } from '#/api/common';
import type { OperationLog } from '#/api/monitor/operlog/model';
import { Page, useVbenDrawer } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { Modal, Space } from 'ant-design-vue';
import {
addSortParams,
useVbenVxeGrid,
vxeCheckboxChecked,
} from '#/adapter/vxe-table';
import {
operLogClean,
operLogDelete,
operLogExport,
operLogList,
} from '#/api/monitor/operlog';
import { commonDownloadExcel } from '#/utils/file/download';
import { confirmDeleteModal } from '#/utils/modal';
import { columns, querySchema } from './data';
import operationPreviewDrawer from './operation-preview-drawer.vue';
const formOptions: VbenFormProps = {
commonConfig: {
labelWidth: 80,
componentProps: {
allowClear: true,
},
},
schema: querySchema(),
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
// 日期选择格式化
fieldMappingTime: [
[
'createTime',
['params[beginTime]', 'params[endTime]'],
['YYYY-MM-DD 00:00:00', 'YYYY-MM-DD 23:59:59'],
],
],
};
const gridOptions: VxeGridProps<OperationLog> = {
checkboxConfig: {
// 高亮
highlight: true,
// 翻页时保留选中状态
reserve: true,
// 点击行选中
trigger: 'row',
},
columns,
height: 'auto',
keepSource: true,
pagerConfig: {},
proxyConfig: {
ajax: {
query: async ({ page, sorts }, formValues = {}) => {
const params: PageQuery = {
pageNum: page.currentPage,
pageSize: page.pageSize,
...formValues,
};
// 添加排序参数
addSortParams(params, sorts);
return await operLogList(params);
},
},
},
rowConfig: {
keyField: 'operId',
},
sortConfig: {
// 远程排序
remote: true,
// 支持多字段排序 默认关闭
multiple: true,
},
id: 'monitor-operlog-index',
};
const [BasicTable, tableApi] = useVbenVxeGrid({
formOptions,
gridOptions,
gridEvents: {
// 排序 重新请求接口
sortChange: () => tableApi.query(),
},
});
const [OperationPreviewDrawer, drawerApi] = useVbenDrawer({
connectedComponent: operationPreviewDrawer,
});
/**
* 预览
* @param record 操作日志记录
*/
function handlePreview(record: OperationLog) {
drawerApi.setData({ record });
drawerApi.open();
}
/**
* 清空全部日志
*/
function handleClear() {
confirmDeleteModal({
onValidated: async () => {
await operLogClean();
await tableApi.reload();
},
});
}
/**
* 删除日志
*/
async function handleDelete() {
const rows = tableApi.grid.getCheckboxRecords();
const ids = rows.map((row: OperationLog) => row.operId);
Modal.confirm({
title: '提示',
okType: 'danger',
content: `确认删除选中的${ids.length}条操作日志吗?`,
onOk: async () => {
await operLogDelete(ids);
await tableApi.query();
},
});
}
function handleDownloadExcel() {
commonDownloadExcel(operLogExport, '操作日志', tableApi.formApi.form.values, {
fieldMappingTime: formOptions.fieldMappingTime,
});
}
</script>
<template>
<Page :auto-content-height="true">
<BasicTable table-title="操作日志列表">
<template #toolbar-tools>
<Space>
<a-button
v-access:code="['monitor:operlog:remove']"
@click="handleClear"
>
{{ $t('pages.common.clear') }}
</a-button>
<a-button
v-access:code="['monitor:operlog:export']"
@click="handleDownloadExcel"
>
{{ $t('pages.common.export') }}
</a-button>
<a-button
:disabled="!vxeCheckboxChecked(tableApi)"
danger
type="primary"
v-access:code="['monitor:operlog:remove']"
@click="handleDelete"
>
{{ $t('pages.common.delete') }}
</a-button>
</Space>
</template>
<template #action="{ row }">
<ghost-button
v-access:code="['monitor:operlog:list']"
@click.stop="handlePreview(row)"
>
{{ $t('pages.common.preview') }}
</ghost-button>
</template>
</BasicTable>
<OperationPreviewDrawer />
</Page>
</template>

View File

@@ -0,0 +1,94 @@
<script setup lang="ts">
import type { OperationLog } from '#/api/monitor/operlog/model';
import { computed, shallowRef } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { DictEnum } from '@vben/constants';
import { Descriptions, DescriptionsItem, Tag } from 'ant-design-vue';
import {
renderDict,
renderHttpMethodTag,
renderJsonPreview,
} from '#/utils/render';
const [BasicDrawer, drawerApi] = useVbenDrawer({
onOpenChange: handleOpenChange,
onClosed() {
currentLog.value = null;
},
});
const currentLog = shallowRef<null | OperationLog>(null);
function handleOpenChange(open: boolean) {
if (!open) {
return null;
}
const { record } = drawerApi.getData() as { record: OperationLog };
currentLog.value = record;
}
const actionInfo = computed(() => {
if (!currentLog.value) {
return '-';
}
const data = currentLog.value;
return `账号: ${data.operName} / ${data.deptName} / ${data.operIp} / ${data.operLocation}`;
});
</script>
<template>
<BasicDrawer :footer="false" class="w-[600px]" title="查看日志">
<Descriptions v-if="currentLog" size="small" bordered :column="1">
<DescriptionsItem label="日志编号" :label-style="{ minWidth: '120px' }">
{{ currentLog.operId }}
</DescriptionsItem>
<DescriptionsItem label="操作结果">
<component
:is="renderDict(currentLog.status, DictEnum.SYS_COMMON_STATUS)"
/>
</DescriptionsItem>
<DescriptionsItem label="操作模块">
<div class="flex items-center">
<Tag>{{ currentLog.title }}</Tag>
<component
:is="renderDict(currentLog.businessType, DictEnum.SYS_OPER_TYPE)"
/>
</div>
</DescriptionsItem>
<DescriptionsItem label="操作信息">
{{ actionInfo }}
</DescriptionsItem>
<DescriptionsItem label="请求信息">
<component :is="renderHttpMethodTag(currentLog.requestMethod)" />
{{ currentLog.operUrl }}
</DescriptionsItem>
<DescriptionsItem v-if="currentLog.errorMsg" label="异常信息">
<span class="font-semibold text-red-600">
{{ currentLog.errorMsg }}
</span>
</DescriptionsItem>
<DescriptionsItem label="方法">
{{ currentLog.method }}
</DescriptionsItem>
<DescriptionsItem label="请求参数">
<div class="max-h-[300px] overflow-y-auto">
<component :is="renderJsonPreview(currentLog.operParam)" />
</div>
</DescriptionsItem>
<DescriptionsItem v-if="currentLog.jsonResult" label="响应参数">
<div class="max-h-[300px] overflow-y-auto">
<component :is="renderJsonPreview(currentLog.jsonResult)" />
</div>
</DescriptionsItem>
<DescriptionsItem label="请求耗时">
{{ `${currentLog.costTime} ms` }}
</DescriptionsItem>
<DescriptionsItem label="操作时间">
{{ `${currentLog.operTime}` }}
</DescriptionsItem>
</Descriptions>
</BasicDrawer>
</template>