feat: 操作日志
This commit is contained in:
@@ -1,20 +1,20 @@
|
|||||||
// https://umijs.org/config/
|
// https://umijs.org/config/
|
||||||
|
|
||||||
import { join } from "node:path";
|
import { join } from 'node:path';
|
||||||
import { defineConfig } from "@umijs/max";
|
import { defineConfig } from '@umijs/max';
|
||||||
import defaultSettings from "./defaultSettings";
|
import defaultSettings from './defaultSettings';
|
||||||
import proxy from "./proxy";
|
import proxy from './proxy';
|
||||||
|
|
||||||
import routes from "./routes";
|
import routes from './routes';
|
||||||
|
|
||||||
const { UMI_ENV = "dev" } = process.env;
|
const { UMI_ENV = 'dev' } = process.env;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name 使用公共路径
|
* @name 使用公共路径
|
||||||
* @description 部署时的路径,如果部署在非根目录下,需要配置这个变量
|
* @description 部署时的路径,如果部署在非根目录下,需要配置这个变量
|
||||||
* @doc https://umijs.org/docs/api/config#publicpath
|
* @doc https://umijs.org/docs/api/config#publicpath
|
||||||
*/
|
*/
|
||||||
const PUBLIC_PATH: string = "/";
|
const PUBLIC_PATH: string = '/';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
/**
|
/**
|
||||||
@@ -83,7 +83,7 @@ export default defineConfig({
|
|||||||
* @name layout 插件
|
* @name layout 插件
|
||||||
* @doc https://umijs.org/docs/max/layout-menu
|
* @doc https://umijs.org/docs/max/layout-menu
|
||||||
*/
|
*/
|
||||||
title: "Ant Design Pro",
|
title: 'Ant Design Pro',
|
||||||
layout: {
|
layout: {
|
||||||
locale: true,
|
locale: true,
|
||||||
...defaultSettings,
|
...defaultSettings,
|
||||||
@@ -94,8 +94,8 @@ export default defineConfig({
|
|||||||
* @doc https://umijs.org/docs/max/moment2dayjs
|
* @doc https://umijs.org/docs/max/moment2dayjs
|
||||||
*/
|
*/
|
||||||
moment2dayjs: {
|
moment2dayjs: {
|
||||||
preset: "antd",
|
preset: 'antd',
|
||||||
plugins: ["duration"],
|
plugins: ['duration'],
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @name 国际化插件
|
* @name 国际化插件
|
||||||
@@ -103,7 +103,7 @@ export default defineConfig({
|
|||||||
*/
|
*/
|
||||||
locale: {
|
locale: {
|
||||||
// default zh-CN
|
// default zh-CN
|
||||||
default: "zh-CN",
|
default: 'zh-CN',
|
||||||
antd: true,
|
antd: true,
|
||||||
// default true, when it is true, will use `navigator.language` overwrite default
|
// default true, when it is true, will use `navigator.language` overwrite default
|
||||||
baseNavigator: true,
|
baseNavigator: true,
|
||||||
@@ -119,7 +119,7 @@ export default defineConfig({
|
|||||||
theme: {
|
theme: {
|
||||||
cssVar: true,
|
cssVar: true,
|
||||||
token: {
|
token: {
|
||||||
fontFamily: "AlibabaSans, sans-serif",
|
fontFamily: 'AlibabaSans, sans-serif',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -142,30 +142,33 @@ export default defineConfig({
|
|||||||
*/
|
*/
|
||||||
headScripts: [
|
headScripts: [
|
||||||
// 解决首次加载时白屏的问题
|
// 解决首次加载时白屏的问题
|
||||||
{ src: join(PUBLIC_PATH, "scripts/loading.js"), async: true },
|
{ src: join(PUBLIC_PATH, 'scripts/loading.js'), async: true },
|
||||||
],
|
],
|
||||||
//================ pro 插件配置 =================
|
//================ pro 插件配置 =================
|
||||||
presets: ["umi-presets-pro"],
|
presets: ['umi-presets-pro'],
|
||||||
/**
|
/**
|
||||||
* @name openAPI 插件的配置
|
* @name openAPI 插件的配置
|
||||||
* @description 基于 openapi 的规范生成serve 和mock,能减少很多样板代码
|
* @description 基于 openapi 的规范生成serve 和mock,能减少很多样板代码
|
||||||
* @doc https://pro.ant.design/zh-cn/docs/openapi/
|
* @doc https://pro.ant.design/zh-cn/docs/openapi/
|
||||||
*/
|
*/
|
||||||
openAPI: [
|
// openAPI: [
|
||||||
// {
|
// // {
|
||||||
// requestLibPath: "import { request } from '@umijs/max'",
|
// // requestLibPath: "import { request } from '@umijs/max'",
|
||||||
// schemaPath: join(__dirname, "oneapi/prodapi.json"),
|
// // schemaPath: join(__dirname, "oneapi/prodapi.json"),
|
||||||
// mock: false,
|
// // mock: false,
|
||||||
// projectName: "prodApi",
|
// // projectName: "prodApi",
|
||||||
// },
|
// // },
|
||||||
// {schemaPath: "./docs/apifox-api.json",
|
// // {schemaPath: "./docs/apifox-api.json",
|
||||||
// requestLibPath: "import { request } from '@umijs/max'",
|
// // requestLibPath: "import { request } from '@umijs/max'",
|
||||||
// schemaPath: join(__dirname, "oneapi.json"),
|
// // schemaPath: join(__dirname, "oneapi.json"),
|
||||||
// projectName: "login",
|
// // projectName: "login",
|
||||||
// },
|
// // },
|
||||||
],
|
// ],
|
||||||
|
codeSplitting: {
|
||||||
|
jsStrategy: 'granularChunks',
|
||||||
|
},
|
||||||
mock: {
|
mock: {
|
||||||
include: ["mock/**/*", "src/pages/**/_mock.ts"],
|
include: ['mock/**/*', 'src/pages/**/_mock.ts'],
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @name 是否开启 mako
|
* @name 是否开启 mako
|
||||||
@@ -177,6 +180,6 @@ export default defineConfig({
|
|||||||
requestRecord: {},
|
requestRecord: {},
|
||||||
exportStatic: {},
|
exportStatic: {},
|
||||||
define: {
|
define: {
|
||||||
"process.env.CI": process.env.CI,
|
'process.env.CI': process.env.CI,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "cross-env ANALYZE=1 max build",
|
"analyze": "cross-env ANALYZE=1 max build",
|
||||||
"build": "max build",
|
"build": "max build",
|
||||||
"deploy": "npm run build && npm run gh-pages",
|
"deploy": "pnpm run build && npm run gh-pages",
|
||||||
"dev": "npm run start:dev",
|
"dev": "pnpm run start:dev",
|
||||||
"gh-pages": "gh-pages -d dist",
|
"gh-pages": "gh-pages -d dist",
|
||||||
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
|
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
|
||||||
"postinstall": "max setup",
|
"postinstall": "max setup",
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -14075,14 +14075,14 @@ snapshots:
|
|||||||
|
|
||||||
'@loadable/component@5.15.2(react@18.3.1)':
|
'@loadable/component@5.15.2(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.23.6
|
'@babel/runtime': 7.28.4
|
||||||
hoist-non-react-statics: 3.3.2
|
hoist-non-react-statics: 3.3.2
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
|
|
||||||
'@loadable/component@5.15.2(react@19.1.1)':
|
'@loadable/component@5.15.2(react@19.1.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.23.6
|
'@babel/runtime': 7.28.4
|
||||||
hoist-non-react-statics: 3.3.2
|
hoist-non-react-statics: 3.3.2
|
||||||
react: 19.1.1
|
react: 19.1.1
|
||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
@@ -15402,7 +15402,7 @@ snapshots:
|
|||||||
|
|
||||||
'@umijs/history@5.3.1':
|
'@umijs/history@5.3.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.23.6
|
'@babel/runtime': 7.28.4
|
||||||
query-string: 6.14.1
|
query-string: 6.14.1
|
||||||
|
|
||||||
'@umijs/lint@4.4.12(eslint@8.35.0)(jest@30.1.3(@types/node@24.3.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.3.1)(typescript@5.9.2)))(stylelint@14.8.2)(typescript@5.9.2)':
|
'@umijs/lint@4.4.12(eslint@8.35.0)(jest@30.1.3(@types/node@24.3.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.3.1)(typescript@5.9.2)))(stylelint@14.8.2)(typescript@5.9.2)':
|
||||||
@@ -16597,7 +16597,7 @@ snapshots:
|
|||||||
|
|
||||||
babel-plugin-macros@2.6.1:
|
babel-plugin-macros@2.6.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.4.5
|
'@babel/runtime': 7.28.4
|
||||||
cosmiconfig: 5.2.1
|
cosmiconfig: 5.2.1
|
||||||
resolve: 1.22.10
|
resolve: 1.22.10
|
||||||
|
|
||||||
@@ -23257,7 +23257,7 @@ snapshots:
|
|||||||
|
|
||||||
react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.23.6
|
'@babel/runtime': 7.28.4
|
||||||
invariant: 2.2.4
|
invariant: 2.2.4
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
@@ -23267,7 +23267,7 @@ snapshots:
|
|||||||
|
|
||||||
react-helmet-async@1.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
|
react-helmet-async@1.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.23.6
|
'@babel/runtime': 7.28.4
|
||||||
invariant: 2.2.4
|
invariant: 2.2.4
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 19.1.1
|
react: 19.1.1
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
// components/EnhancedProTable/EnhancedProTable.tsx
|
// components/EnhancedProTable/EnhancedProTable.tsx
|
||||||
|
|
||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import {
|
||||||
|
type ActionType,
|
||||||
|
type ParamsType,
|
||||||
|
ProColumns,
|
||||||
|
ProTable,
|
||||||
|
TableDropdown,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { Button, Space } from 'antd';
|
||||||
import React, {
|
import React, {
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
useCallback,
|
|
||||||
useMemo,
|
|
||||||
act,
|
act,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
} from "react";
|
useCallback,
|
||||||
import {
|
useMemo,
|
||||||
ProTable,
|
useRef,
|
||||||
ProColumns,
|
useState,
|
||||||
ActionType,
|
} from 'react';
|
||||||
TableDropdown,
|
|
||||||
ParamsType,
|
|
||||||
} from "@ant-design/pro-components";
|
|
||||||
import { Button, Space } from "antd";
|
|
||||||
import {
|
|
||||||
EnhancedProTableProps,
|
|
||||||
BaseRecord,
|
|
||||||
TableAction,
|
|
||||||
ToolbarAction,
|
|
||||||
} from "./types";
|
|
||||||
import {
|
import {
|
||||||
buildTableDropdownMenuItems,
|
buildTableDropdownMenuItems,
|
||||||
handleTableDropdownSelect,
|
|
||||||
formatPaginationTotal,
|
formatPaginationTotal,
|
||||||
} from "@/utils/antd/tableHelpers";
|
handleTableDropdownSelect,
|
||||||
import { PlusOutlined } from "@ant-design/icons";
|
} from '@/utils/antd/tableHelpers';
|
||||||
|
import {
|
||||||
|
type BaseRecord,
|
||||||
|
type EnhancedProTableProps,
|
||||||
|
TableAction,
|
||||||
|
ToolbarAction,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
||||||
props: EnhancedProTableProps<T, U>,
|
props: EnhancedProTableProps<T, U>,
|
||||||
ref: React.Ref<ActionType | undefined> | undefined
|
ref: React.Ref<ActionType | undefined> | undefined,
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
columns,
|
columns,
|
||||||
@@ -87,7 +88,7 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[showSelection]
|
[showSelection],
|
||||||
);
|
);
|
||||||
|
|
||||||
const toolBarRender = useCallback(
|
const toolBarRender = useCallback(
|
||||||
@@ -96,7 +97,7 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
rows: {
|
rows: {
|
||||||
selectedRowKeys?: (string | number)[] | undefined;
|
selectedRowKeys?: (string | number)[] | undefined;
|
||||||
selectedRows?: T[] | undefined;
|
selectedRows?: T[] | undefined;
|
||||||
}
|
},
|
||||||
) => {
|
) => {
|
||||||
const toolbarElements =
|
const toolbarElements =
|
||||||
toolbarActions?.map((action) => {
|
toolbarActions?.map((action) => {
|
||||||
@@ -125,7 +126,7 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
// ];
|
// ];
|
||||||
return toolbarElements;
|
return toolbarElements;
|
||||||
},
|
},
|
||||||
[toolbarActions]
|
[toolbarActions],
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<ProTable<T, U>
|
<ProTable<T, U>
|
||||||
@@ -140,9 +141,9 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
showSorterTooltip
|
showSorterTooltip
|
||||||
tableAlertRender={tableAlertRender}
|
tableAlertRender={tableAlertRender}
|
||||||
// tableAlertOptionRender={tableAlertOptionRender}
|
// tableAlertOptionRender={tableAlertOptionRender}
|
||||||
scroll={{ x: "max-content" }}
|
scroll={{ x: 'max-content' }}
|
||||||
search={{
|
search={{
|
||||||
labelWidth: "auto",
|
labelWidth: 'auto',
|
||||||
defaultCollapsed: false,
|
defaultCollapsed: false,
|
||||||
...restProps.search,
|
...restProps.search,
|
||||||
}}
|
}}
|
||||||
@@ -156,6 +157,7 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
pagination={{
|
pagination={{
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
showQuickJumper: true,
|
showQuickJumper: true,
|
||||||
|
pageSize: 10,
|
||||||
showTotal: formatPaginationTotal,
|
showTotal: formatPaginationTotal,
|
||||||
...restProps.pagination,
|
...restProps.pagination,
|
||||||
}}
|
}}
|
||||||
@@ -165,7 +167,7 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
|
|
||||||
export default forwardRef(EnhancedProTable) as <
|
export default forwardRef(EnhancedProTable) as <
|
||||||
T extends BaseRecord,
|
T extends BaseRecord,
|
||||||
U extends ParamsType = any
|
U extends ParamsType = any,
|
||||||
>(
|
>(
|
||||||
props: EnhancedProTableProps<T, U> & { ref?: React.Ref<ActionType> }
|
props: EnhancedProTableProps<T, U> & { ref?: React.Ref<ActionType> },
|
||||||
) => React.ReactElement;
|
) => React.ReactElement;
|
||||||
|
|||||||
72
src/components/ModalDescriptions/index.tsx
Normal file
72
src/components/ModalDescriptions/index.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import {
|
||||||
|
ProDescriptions,
|
||||||
|
type ProDescriptionsItemProps,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { type DescriptionsProps, Modal } from 'antd';
|
||||||
|
import React, {
|
||||||
|
forwardRef,
|
||||||
|
useCallback,
|
||||||
|
useImperativeHandle,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
export interface DescriptionsFormRef {
|
||||||
|
open: (data?: Record<string, any>) => void;
|
||||||
|
close: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
columns: ProDescriptionsItemProps<Record<string, any>, 'text'>[];
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModalDescriptions = forwardRef((props: Props, ref) => {
|
||||||
|
const { columns, title } = props;
|
||||||
|
const [visible, setVisible] = useState<boolean>(false);
|
||||||
|
const [data, setData] = useState<DescriptionsProps['items']>([]);
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
open: (data: DescriptionsProps['items']) => {
|
||||||
|
console.log(data);
|
||||||
|
if (data) {
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
setVisible(true);
|
||||||
|
},
|
||||||
|
close: () => setVisible(false),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const changeVisible = useCallback(
|
||||||
|
(flag: boolean) => {
|
||||||
|
setVisible(flag);
|
||||||
|
},
|
||||||
|
[visible],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={visible}
|
||||||
|
width={800}
|
||||||
|
onCancel={() => changeVisible(false)}
|
||||||
|
footer={null}
|
||||||
|
>
|
||||||
|
<ProDescriptions
|
||||||
|
labelStyle={{ minWidth: '200px' }}
|
||||||
|
columns={columns}
|
||||||
|
dataSource={data}
|
||||||
|
bordered
|
||||||
|
column={1}
|
||||||
|
size="small"
|
||||||
|
title={title}
|
||||||
|
// tooltip="操作日志详情"
|
||||||
|
></ProDescriptions>
|
||||||
|
{/* <Descriptions
|
||||||
|
title="操作日志"
|
||||||
|
column={1}
|
||||||
|
bordered
|
||||||
|
size="small"
|
||||||
|
items={data}
|
||||||
|
/> */}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default React.memo(ModalDescriptions);
|
||||||
134
src/pages/system/log/operate/config.tsx
Normal file
134
src/pages/system/log/operate/config.tsx
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import {
|
||||||
|
type ProColumns,
|
||||||
|
type ProDescriptionsItemProps,
|
||||||
|
ProFormColumnsType,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { DescriptionsProps } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import type { OperateLogVO } from '@/services/system/log/operate';
|
||||||
|
export const baseTenantColumns: ProColumns<OperateLogVO>[] = [
|
||||||
|
{
|
||||||
|
title: '日志编号',
|
||||||
|
dataIndex: 'id',
|
||||||
|
tip: '日志编号',
|
||||||
|
width: 100,
|
||||||
|
hideInSearch: true, // 在搜索表单中隐藏
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作人',
|
||||||
|
dataIndex: 'userName',
|
||||||
|
tip: '操作人', // 提示信息
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作模块',
|
||||||
|
dataIndex: 'type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作名',
|
||||||
|
dataIndex: 'subType',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作内容',
|
||||||
|
dataIndex: 'action',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '操作时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
valueType: 'dateRange',
|
||||||
|
search: {
|
||||||
|
transform: (value) => {
|
||||||
|
return {
|
||||||
|
[`createTime[0]`]: dayjs(value[0])
|
||||||
|
.startOf('day')
|
||||||
|
.format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
[`createTime[1]`]: dayjs(value[1])
|
||||||
|
.endOf('day')
|
||||||
|
.format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render: (_, record: OperateLogVO) =>
|
||||||
|
dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '业务编号',
|
||||||
|
dataIndex: 'bizId',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作 IP',
|
||||||
|
dataIndex: 'userIp',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const descriptionsColumns = (): ProDescriptionsItemProps<
|
||||||
|
Record<string, any>,
|
||||||
|
'text'
|
||||||
|
>[] => [
|
||||||
|
{
|
||||||
|
title: '日志主键',
|
||||||
|
key: 'id',
|
||||||
|
dataIndex: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '链路追踪',
|
||||||
|
key: 'traceId',
|
||||||
|
dataIndex: 'traceId',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作人编号',
|
||||||
|
key: 'userId',
|
||||||
|
dataIndex: 'userId',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作人名字',
|
||||||
|
key: 'userName',
|
||||||
|
dataIndex: 'userName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作人 IP',
|
||||||
|
key: 'userIp',
|
||||||
|
dataIndex: 'userIp',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作人 UA',
|
||||||
|
key: 'userAgent',
|
||||||
|
dataIndex: 'userAgent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作模块',
|
||||||
|
key: 'type',
|
||||||
|
dataIndex: 'type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作名',
|
||||||
|
key: 'subType',
|
||||||
|
dataIndex: 'subType',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作内容',
|
||||||
|
key: 'action',
|
||||||
|
dataIndex: 'action',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作拓展参数',
|
||||||
|
key: 'extra',
|
||||||
|
dataIndex: 'extra',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '请求 URL',
|
||||||
|
key: 'requestUrl',
|
||||||
|
dataIndex: 'requestUrl',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作时间',
|
||||||
|
key: 'createTime',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '业务编号',
|
||||||
|
key: 'bizId',
|
||||||
|
dataIndex: 'bizId',
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,5 +1,70 @@
|
|||||||
|
import type { ActionType, ProColumns } from '@ant-design/pro-components';
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
import EnhancedProTable from '@/components/EnhancedProTable';
|
||||||
|
import ModalDescriptions, {
|
||||||
|
type DescriptionsFormRef,
|
||||||
|
} from '@/components/ModalDescriptions';
|
||||||
|
import {
|
||||||
|
getOperateLogPage,
|
||||||
|
type OperateLogVO,
|
||||||
|
} from '@/services/system/log/operate';
|
||||||
|
import { baseTenantColumns, descriptionsColumns } from './config';
|
||||||
|
|
||||||
const SyStemLogOperate = () => {
|
const SyStemLogOperate = () => {
|
||||||
return <>SyStemLogOperate</>;
|
const tableRef = useRef<ActionType>(null);
|
||||||
|
const descriptionsRef = useRef<DescriptionsFormRef>(null);
|
||||||
|
const onFetch = async (
|
||||||
|
params: OperateLogVO & {
|
||||||
|
pageSize?: number;
|
||||||
|
current?: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const data = await getOperateLogPage({
|
||||||
|
...params,
|
||||||
|
pageNo: params.current,
|
||||||
|
pageSize: params.pageSize,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
data: data.list,
|
||||||
|
success: true,
|
||||||
|
total: data.total,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDetail = (record: OperateLogVO) => {
|
||||||
|
descriptionsRef.current?.open(record);
|
||||||
|
};
|
||||||
|
|
||||||
|
const actionColumns: ProColumns<OperateLogVO> = {
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'option',
|
||||||
|
valueType: 'option',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 80,
|
||||||
|
render: (text: React.ReactNode, record: OperateLogVO) => [
|
||||||
|
<a key="editable" onClick={() => handleDetail(record)}>
|
||||||
|
详情
|
||||||
|
</a>,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const columns = [...baseTenantColumns, actionColumns];
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EnhancedProTable<OperateLogVO>
|
||||||
|
ref={tableRef}
|
||||||
|
columns={columns}
|
||||||
|
request={onFetch}
|
||||||
|
headerTitle="操作日志"
|
||||||
|
showIndex={false}
|
||||||
|
showSelection={false}
|
||||||
|
/>
|
||||||
|
<ModalDescriptions
|
||||||
|
ref={descriptionsRef}
|
||||||
|
title="操作日志详情"
|
||||||
|
columns={descriptionsColumns()}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SyStemLogOperate;
|
export default React.memo(SyStemLogOperate);
|
||||||
|
|||||||
@@ -1,33 +1,37 @@
|
|||||||
import { type TenantVO, deleteTenant } from "@/services/system/tenant/list";
|
import type {
|
||||||
import { ProColumns, ProFormColumnsType } from "@ant-design/pro-components";
|
ProColumns,
|
||||||
import { DatePicker, Modal, Popconfirm } from "antd";
|
ProFormColumnsType,
|
||||||
import { FormInstance } from "antd/lib";
|
} from '@ant-design/pro-components';
|
||||||
import dayjs from "dayjs";
|
import { DatePicker, Modal, Popconfirm } from 'antd';
|
||||||
|
import { FormInstance } from 'antd/lib';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { deleteTenant, type TenantVO } from '@/services/system/tenant/list';
|
||||||
|
import { getTenantPackageList } from '@/services/system/tenant/package';
|
||||||
|
|
||||||
export const baseTenantColumns: ProColumns<TenantVO>[] = [
|
export const baseTenantColumns: ProColumns<TenantVO>[] = [
|
||||||
{
|
{
|
||||||
title: "租户编号",
|
title: '租户编号',
|
||||||
dataIndex: "id",
|
dataIndex: 'id',
|
||||||
tip: "租户编号",
|
tip: '租户编号',
|
||||||
width: 100,
|
width: 100,
|
||||||
hideInSearch: true, // 在搜索表单中隐藏
|
hideInSearch: true, // 在搜索表单中隐藏
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "租户名",
|
title: '租户名',
|
||||||
dataIndex: "name",
|
dataIndex: 'name',
|
||||||
tip: "租户名", // 提示信息
|
tip: '租户名', // 提示信息
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "租户套餐",
|
title: '租户套餐',
|
||||||
dataIndex: "packageId",
|
dataIndex: 'packageId',
|
||||||
valueType: "select",
|
valueType: 'select',
|
||||||
|
hideInSearch: true, // 在搜索表单中隐藏
|
||||||
request: async () => {
|
request: async () => {
|
||||||
return [
|
const packageList: { id: number; name: string }[] =
|
||||||
{
|
await getTenantPackageList();
|
||||||
label: "默认套餐",
|
console.log(packageList);
|
||||||
value: 1,
|
packageList.map((item) => ({ label: item.name, value: item.id }));
|
||||||
},
|
return packageList.map((item) => ({ label: item.name, value: item.id }));
|
||||||
];
|
|
||||||
},
|
},
|
||||||
// valueEnum: {
|
// valueEnum: {
|
||||||
// all: { text: "全部", status: "Default" },
|
// all: { text: "全部", status: "Default" },
|
||||||
@@ -36,60 +40,67 @@ export const baseTenantColumns: ProColumns<TenantVO>[] = [
|
|||||||
// },
|
// },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "联系人",
|
title: '联系人',
|
||||||
dataIndex: "contactName",
|
dataIndex: 'contactName',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "联系手机",
|
title: '联系手机',
|
||||||
dataIndex: "contactMobile",
|
dataIndex: 'contactMobile',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "账号额度",
|
title: '账号额度',
|
||||||
dataIndex: "accountCount",
|
dataIndex: 'accountCount',
|
||||||
hideInSearch: true, // 在搜索表单中隐藏
|
hideInSearch: true, // 在搜索表单中隐藏
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "过期时间",
|
title: '过期时间',
|
||||||
dataIndex: "expireTime",
|
dataIndex: 'expireTime',
|
||||||
valueType: "dateTime",
|
valueType: 'dateTime',
|
||||||
|
|
||||||
hideInSearch: true, // 在搜索表单中隐藏
|
hideInSearch: true, // 在搜索表单中隐藏
|
||||||
},
|
},
|
||||||
{ title: "绑定域名", dataIndex: "website", width: 100 },
|
{ title: '绑定域名', dataIndex: 'website', width: 100, hideInSearch: true },
|
||||||
{
|
{
|
||||||
title: "租户状态",
|
title: '租户状态',
|
||||||
dataIndex: "status",
|
dataIndex: 'status',
|
||||||
valueType: "select",
|
valueType: 'select',
|
||||||
valueEnum: {
|
fieldProps: {
|
||||||
all: { text: "全部", status: "Default" },
|
options: [
|
||||||
open: { text: "未解决", status: "Error" },
|
{
|
||||||
closed: { text: "已解决", status: "Success" },
|
label: '开启',
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '关闭',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "创建时间",
|
title: '创建时间',
|
||||||
dataIndex: "createTime",
|
dataIndex: 'createTime',
|
||||||
valueType: "dateRange",
|
valueType: 'dateRange',
|
||||||
search: {
|
search: {
|
||||||
transform: (value) => {
|
transform: (value) => {
|
||||||
return [`${value[0]} 00:00:00`, `${value[1]} 00:00:00`];
|
return [`${value[0]} 00:00:00`, `${value[1]} 00:00:00`];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render: (_, record: TenantVO) =>
|
render: (_, record: TenantVO) =>
|
||||||
dayjs(record.createTime).format("YYYY-MM-DD HH:mm:ss"),
|
dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const formColumns = (type: string): ProFormColumnsType[] => [
|
export const formColumns = (type: string): ProFormColumnsType[] => [
|
||||||
{
|
{
|
||||||
title: "租户名",
|
title: '租户名',
|
||||||
dataIndex: "name",
|
dataIndex: 'name',
|
||||||
tip: "租户名", // 提示信息
|
tip: '租户名', // 提示信息
|
||||||
formItemProps: {
|
formItemProps: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "请输入用户名",
|
message: '请输入用户名',
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// min: 2,
|
// min: 2,
|
||||||
@@ -100,121 +111,121 @@ export const formColumns = (type: string): ProFormColumnsType[] => [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "租户套餐",
|
title: '租户套餐',
|
||||||
dataIndex: "packageId",
|
dataIndex: 'packageId',
|
||||||
valueType: "select",
|
valueType: 'select',
|
||||||
formItemProps: {
|
formItemProps: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "请选择租户套餐",
|
message: '请选择租户套餐',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
fieldProps: {
|
fieldProps: {
|
||||||
placeholder: "请选择套餐类型",
|
placeholder: '请选择套餐类型',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: "普通套餐",
|
label: '普通套餐',
|
||||||
value: 111,
|
value: 111,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "联系人",
|
title: '联系人',
|
||||||
dataIndex: "contactName",
|
dataIndex: 'contactName',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "联系手机",
|
title: '联系手机',
|
||||||
dataIndex: "contactMobile",
|
dataIndex: 'contactMobile',
|
||||||
formItemProps: {
|
formItemProps: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "请输入联系手机",
|
message: '请输入联系手机',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "用户名称",
|
title: '用户名称',
|
||||||
dataIndex: "username",
|
dataIndex: 'username',
|
||||||
hideInForm: type === "update",
|
hideInForm: type === 'update',
|
||||||
formItemProps: {
|
formItemProps: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "请输入用户名称",
|
message: '请输入用户名称',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9]+$/,
|
pattern: /^[a-zA-Z0-9]+$/,
|
||||||
message: "用户账号由 0-9、a-z、A-Z 组成",
|
message: '用户账号由 0-9、a-z、A-Z 组成',
|
||||||
},
|
},
|
||||||
// 用户账号由 数字、字母组成
|
// 用户账号由 数字、字母组成
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "用户密码",
|
title: '用户密码',
|
||||||
dataIndex: "password",
|
dataIndex: 'password',
|
||||||
valueType: "password",
|
valueType: 'password',
|
||||||
hideInForm: type === "update",
|
hideInForm: type === 'update',
|
||||||
fieldProps: {
|
fieldProps: {
|
||||||
placeholder: "请输入用户密码",
|
placeholder: '请输入用户密码',
|
||||||
autoComplete: "new-password",
|
autoComplete: 'new-password',
|
||||||
},
|
},
|
||||||
formItemProps: {
|
formItemProps: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "请输入用户密码",
|
message: '请输入用户密码',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
min: 4,
|
min: 4,
|
||||||
max: 16,
|
max: 16,
|
||||||
message: "密码长度为4-16个字符",
|
message: '密码长度为4-16个字符',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "账号额度",
|
title: '账号额度',
|
||||||
dataIndex: "accountCount",
|
dataIndex: 'accountCount',
|
||||||
valueType: "digit",
|
valueType: 'digit',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "过期时间",
|
title: '过期时间',
|
||||||
dataIndex: "expireTime",
|
dataIndex: 'expireTime',
|
||||||
valueType: "date",
|
valueType: 'date',
|
||||||
|
|
||||||
fieldProps: {
|
fieldProps: {
|
||||||
placeholder: "请选择过期时间",
|
placeholder: '请选择过期时间',
|
||||||
format: "YYYY-MM-DD",
|
format: 'YYYY-MM-DD',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ title: "绑定域名", dataIndex: "website" },
|
{ title: '绑定域名', dataIndex: 'website' },
|
||||||
{
|
{
|
||||||
title: "租户状态",
|
title: '租户状态',
|
||||||
dataIndex: "status",
|
dataIndex: 'status',
|
||||||
valueType: "radio",
|
valueType: 'radio',
|
||||||
formItemProps: {
|
formItemProps: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: "请选择租户状态",
|
message: '请选择租户状态',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
fieldProps: {
|
fieldProps: {
|
||||||
placeholder: "请选择套餐类型",
|
placeholder: '请选择套餐类型',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: "启用",
|
label: '启用',
|
||||||
value: 1,
|
value: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "禁用",
|
label: '禁用',
|
||||||
value: 0,
|
value: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
37
src/services/system/log/operate.ts
Normal file
37
src/services/system/log/operate.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { request } from "@umijs/max";
|
||||||
|
|
||||||
|
export type OperateLogVO = {
|
||||||
|
id: number;
|
||||||
|
traceId: string;
|
||||||
|
userType: number;
|
||||||
|
userId: number;
|
||||||
|
userName: string;
|
||||||
|
type: string;
|
||||||
|
subType: string;
|
||||||
|
bizId: number;
|
||||||
|
action: string;
|
||||||
|
extra: string;
|
||||||
|
requestMethod: string;
|
||||||
|
requestUrl: string;
|
||||||
|
userIp: string;
|
||||||
|
userAgent: string;
|
||||||
|
creator: string;
|
||||||
|
creatorName: string;
|
||||||
|
createTime: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询操作日志列表
|
||||||
|
// export const getOperateLogPage = (params: PageParam) => {
|
||||||
|
// return request.get({ url: '/system/operate-log/page', params })
|
||||||
|
// }
|
||||||
|
|
||||||
|
export async function getOperateLogPage(params: PageParam) {
|
||||||
|
return request("/system/operate-log/page", {
|
||||||
|
method: "GET",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// // 导出操作日志
|
||||||
|
// export const exportOperateLog = (params: any) => {
|
||||||
|
// return request.download({ url: "/system/operate-log/export", params });
|
||||||
|
// };
|
||||||
Reference in New Issue
Block a user