add
This commit is contained in:
parent
9685d94b96
commit
538f2f5b38
|
@ -193,91 +193,5 @@ export class SystemLogRouter {
|
|||
}
|
||||
});
|
||||
}),
|
||||
|
||||
// 高级搜索日志
|
||||
searchLogs: this.trpc.procedure
|
||||
.input(z.object({
|
||||
page: z.number().default(1),
|
||||
pageSize: z.number().default(20),
|
||||
level: z.enum(['info', 'warning', 'error', 'debug']).optional(),
|
||||
module: z.string().optional(),
|
||||
action: z.string().optional(),
|
||||
operatorId: z.string().optional(),
|
||||
targetId: z.string().optional(),
|
||||
targetType: z.string().optional(),
|
||||
status: z.enum(['success', 'failure']).optional(),
|
||||
startTime: z.string().optional(),
|
||||
endTime: z.string().optional(),
|
||||
keyword: z.string().optional(),
|
||||
departmentId: z.string().optional(),
|
||||
}))
|
||||
.query(async ({ input }) => {
|
||||
// 构建查询条件
|
||||
const where: Prisma.SystemLogWhereInput = {};
|
||||
|
||||
if (input.level) where.level = input.level;
|
||||
if (input.module) where.module = input.module;
|
||||
if (input.action) where.action = input.action;
|
||||
if (input.operatorId) where.operatorId = input.operatorId;
|
||||
if (input.targetId) where.targetId = input.targetId;
|
||||
if (input.targetType) where.targetType = input.targetType;
|
||||
if (input.status) where.status = input.status;
|
||||
if (input.departmentId) where.departmentId = input.departmentId;
|
||||
|
||||
// 时间范围查询
|
||||
if (input.startTime || input.endTime) {
|
||||
where.timestamp = {};
|
||||
if (input.startTime) where.timestamp.gte = new Date(input.startTime);
|
||||
if (input.endTime) where.timestamp.lte = new Date(input.endTime);
|
||||
}
|
||||
|
||||
// 关键词搜索
|
||||
if (input.keyword) {
|
||||
where.OR = [
|
||||
{ targetName: { contains: input.keyword } },
|
||||
{ action: { contains: input.keyword } },
|
||||
{ module: { contains: input.keyword } },
|
||||
{ errorMessage: { contains: input.keyword } },
|
||||
];
|
||||
}
|
||||
|
||||
// 使用select代替include
|
||||
return this.systemLogService.findManyWithPagination({
|
||||
page: input.page,
|
||||
pageSize: input.pageSize,
|
||||
where,
|
||||
select: {
|
||||
id: true,
|
||||
level: true,
|
||||
module: true,
|
||||
action: true,
|
||||
timestamp: true,
|
||||
operatorId: true,
|
||||
ipAddress: true,
|
||||
targetId: true,
|
||||
targetType: true,
|
||||
targetName: true,
|
||||
details: true,
|
||||
beforeData: true,
|
||||
afterData: true,
|
||||
status: true,
|
||||
errorMessage: true,
|
||||
departmentId: true,
|
||||
operator: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
showname: true,
|
||||
}
|
||||
},
|
||||
department: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}),
|
||||
})
|
||||
}
|
|
@ -47,7 +47,7 @@ const StaffInfoWrite = () => {
|
|||
// 按分组组织字段
|
||||
const fieldGroups = useMemo(() => {
|
||||
if (!fields) return {};
|
||||
return fields.reduce((groups: any, field: any) => {
|
||||
return (fields as any[]).reduce((groups: any, field: any) => {
|
||||
const group = field.group || '其他信息';
|
||||
if (!groups[group]) {
|
||||
groups[group] = [];
|
||||
|
@ -157,28 +157,38 @@ const StaffInfoWrite = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
const onFinish = async (e, values: any) => {
|
||||
// values.preventDefault();
|
||||
e.preventDefault()
|
||||
console.log(values)
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// 创建基础员工记录
|
||||
if (!values.username) {
|
||||
message.error("用户名不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建基础员工记录
|
||||
console.log('准备创建用户,数据:', { username: values.username });
|
||||
const staff = await create.mutateAsync({
|
||||
data: {
|
||||
username: values.username,
|
||||
password: '123456'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('创建员工记录:', staff);
|
||||
// 过滤有效字段并转换值
|
||||
const validEntries = Object.entries(values)
|
||||
.filter(([_, value]) => value !== undefined && value !== null && value !== '')
|
||||
.filter(([key, value]) => key !== 'username' && value !== undefined && value !== null && value !== '')
|
||||
.map(([fieldName, value]) => {
|
||||
const field = fields?.find((f: any) => f.name === fieldName);
|
||||
const field = fields && Array.isArray(fields) ? fields.find((f: any) => f.name === fieldName) : undefined;
|
||||
let processedValue = value;
|
||||
|
||||
// 处理特殊字段类型
|
||||
if (field?.type === 'date' && value instanceof moment) {
|
||||
processedValue = value.format('YYYY-MM-DD');
|
||||
if (field?.type === 'date') {
|
||||
processedValue = value.toString();
|
||||
} else if (field?.type === 'cascader' && Array.isArray(value)) {
|
||||
processedValue = value.join('/');
|
||||
}
|
||||
|
@ -197,8 +207,10 @@ const StaffInfoWrite = () => {
|
|||
})
|
||||
)
|
||||
);
|
||||
console.log('自定义字段提交成功',staff.username);
|
||||
|
||||
message.success("信息提交成功");
|
||||
form.resetFields();
|
||||
} catch (error) {
|
||||
console.error('提交出错:', error);
|
||||
message.error("提交失败,请重试");
|
||||
|
@ -218,7 +230,6 @@ const StaffInfoWrite = () => {
|
|||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={onFinish}
|
||||
onValuesChange={(changedValues) => {
|
||||
if ('hasTrain' in changedValues) {
|
||||
setHasTrain(!!changedValues.hasTrain);
|
||||
|
@ -268,6 +279,7 @@ const StaffInfoWrite = () => {
|
|||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
onClick={(e) => onFinish(e, form.getFieldsValue())}
|
||||
loading={loading}
|
||||
>
|
||||
提交
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, Table, Space, Tag, Form, Select, DatePicker, Input, Button } from 'antd';
|
||||
import { Card, Table, Space, Tag, Form, Select, DatePicker, Input, Button, Modal } from 'antd';
|
||||
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
import { api } from '@nice/client';
|
||||
|
@ -11,239 +11,367 @@ const { Option } = Select;
|
|||
|
||||
// 日志接口定义
|
||||
interface ILog {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
level: string;
|
||||
module: string;
|
||||
action: string;
|
||||
operatorId?: string;
|
||||
operator?: {
|
||||
id: string;
|
||||
username: string;
|
||||
showname?: string;
|
||||
};
|
||||
ipAddress?: string;
|
||||
targetId?: string;
|
||||
targetType?: string;
|
||||
targetName?: string;
|
||||
details?: any;
|
||||
beforeData?: any;
|
||||
afterData?: any;
|
||||
status: string;
|
||||
errorMessage?: string;
|
||||
timestamp: string;
|
||||
level: string;
|
||||
module: string;
|
||||
action: string;
|
||||
operatorId?: string;
|
||||
operator?: {
|
||||
id: string;
|
||||
username: string;
|
||||
showname?: string;
|
||||
};
|
||||
ipAddress?: string;
|
||||
targetId?: string;
|
||||
targetType?: string;
|
||||
targetName?: string;
|
||||
details?: any;
|
||||
beforeData?: any;
|
||||
afterData?: any;
|
||||
status: string;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
// 添加日志的函数
|
||||
export const addLog = async (logData: Omit<ILog, 'id' | 'timestamp'>) => {
|
||||
try {
|
||||
// 使用tRPC发送日志
|
||||
const result = await api.systemLog.create.mutate({
|
||||
...logData,
|
||||
level: logData.level || 'info',
|
||||
status: logData.status || 'success',
|
||||
});
|
||||
|
||||
console.log('日志已写入数据库:', result);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('写入日志失败:', error);
|
||||
// 可以考虑添加本地缓存逻辑
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// 检查 api.systemLog 是否存在
|
||||
if (!api.systemLog) {
|
||||
console.error('systemLog API 不可用');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 使用tRPC发送日志
|
||||
const result = await api.systemLog.create.mutate({
|
||||
...logData,
|
||||
level: logData.level || 'info',
|
||||
status: logData.status || 'success',
|
||||
});
|
||||
|
||||
console.log('日志已写入数据库:', result);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('写入日志失败:', error);
|
||||
// 可以考虑添加本地缓存逻辑
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 用于添加人员操作日志的便捷方法
|
||||
export const addStaffLog = async (
|
||||
action: string,
|
||||
targetId: string,
|
||||
targetName: string,
|
||||
beforeData: any = null,
|
||||
afterData: any = null,
|
||||
status: 'success' | 'failure' = 'success',
|
||||
errorMessage?: string
|
||||
action: string,
|
||||
targetId: string,
|
||||
targetName: string,
|
||||
beforeData: any = null,
|
||||
afterData: any = null,
|
||||
status: 'success' | 'failure' = 'success',
|
||||
errorMessage?: string
|
||||
) => {
|
||||
return api.systemLog.logStaffAction.mutate({
|
||||
action,
|
||||
targetId,
|
||||
targetName,
|
||||
beforeData,
|
||||
afterData,
|
||||
status,
|
||||
errorMessage
|
||||
});
|
||||
return api.systemLog.logStaffAction.mutate({
|
||||
action,
|
||||
targetId,
|
||||
targetName,
|
||||
beforeData,
|
||||
afterData,
|
||||
status,
|
||||
errorMessage
|
||||
});
|
||||
};
|
||||
|
||||
const SystemLogPage = () => {
|
||||
const [form] = Form.useForm();
|
||||
const [queryParams, setQueryParams] = useState({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
// 使用tRPC查询日志
|
||||
const { data, isLoading, refetch } = api.systemLog.getLogs.useQuery({
|
||||
page: queryParams.page,
|
||||
pageSize: queryParams.pageSize
|
||||
});
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleTableChange = (pagination: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
const [form] = Form.useForm();
|
||||
const [queryParams, setQueryParams] = useState({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
where: {}
|
||||
});
|
||||
};
|
||||
|
||||
// 处理表单查询
|
||||
const handleSearch = (values: any) => {
|
||||
const { timeRange, ...rest } = values;
|
||||
|
||||
const params: any = {
|
||||
...rest,
|
||||
page: 1, // 重置到第一页
|
||||
pageSize: queryParams.pageSize,
|
||||
|
||||
// 使用 getLogs API 替代 searchLogs
|
||||
const { data, isLoading, refetch } = api.systemLog.getLogs.useQuery(queryParams, {
|
||||
// 启用数据保留,避免加载时页面闪烁
|
||||
keepPreviousData: true
|
||||
});
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleTableChange = (pagination: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
});
|
||||
};
|
||||
|
||||
// 处理时间范围
|
||||
if (timeRange && timeRange.length === 2) {
|
||||
params.startTime = timeRange[0].startOf('day').toISOString();
|
||||
params.endTime = timeRange[1].endOf('day').toISOString();
|
||||
}
|
||||
|
||||
setQueryParams(params);
|
||||
};
|
||||
|
||||
// 格式化时间显示
|
||||
const formatTime = (timeStr: string) => {
|
||||
return dayjs(timeStr).format('YYYY-MM-DD HH:mm:ss');
|
||||
};
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '操作时间',
|
||||
dataIndex: 'timestamp',
|
||||
key: 'timestamp',
|
||||
render: (text: string) => formatTime(text)
|
||||
},
|
||||
{
|
||||
title: '级别',
|
||||
dataIndex: 'level',
|
||||
key: 'level',
|
||||
render: (text: string) => (
|
||||
<Tag color={
|
||||
text === 'info' ? 'blue' :
|
||||
text === 'warning' ? 'orange' :
|
||||
text === 'error' ? 'red' : 'green'
|
||||
}>
|
||||
{text.toUpperCase()}
|
||||
</Tag>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '模块',
|
||||
dataIndex: 'module',
|
||||
key: 'module',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
key: 'operator',
|
||||
render: (_, record: ILog) => (
|
||||
record.operator ? record.operator.showname || record.operator.username : '-'
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '操作对象',
|
||||
dataIndex: 'targetName',
|
||||
key: 'targetName',
|
||||
render: (text: string) => text || '-'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
render: (text: string) => (
|
||||
<Tag color={text === 'success' ? 'green' : 'red'}>
|
||||
{text === 'success' ? '成功' : '失败'}
|
||||
</Tag>
|
||||
)
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Card>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={handleSearch}
|
||||
className="mb-4"
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<Form.Item name="keyword" label="关键词">
|
||||
<Input placeholder="请输入关键词" allowClear />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="level" label="日志级别">
|
||||
<Select placeholder="请选择级别" allowClear>
|
||||
<Option value="info">信息</Option>
|
||||
<Option value="warning">警告</Option>
|
||||
<Option value="error">错误</Option>
|
||||
<Option value="debug">调试</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="status" label="状态">
|
||||
<Select placeholder="请选择状态" allowClear>
|
||||
<Option value="success">成功</Option>
|
||||
<Option value="failure">失败</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="timeRange" label="时间范围">
|
||||
<RangePicker className="w-full" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Space>
|
||||
<Button onClick={() => form.resetFields()}>重置</Button>
|
||||
<Button type="primary" htmlType="submit" icon={<SearchOutlined />}>
|
||||
查询
|
||||
</Button>
|
||||
<Button icon={<ReloadOutlined />} onClick={() => refetch()}>
|
||||
刷新
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={data?.items || []}
|
||||
rowKey="id"
|
||||
pagination={{
|
||||
current: data?.pagination?.current || 1,
|
||||
pageSize: data?.pagination?.pageSize || 10,
|
||||
total: data?.pagination?.total || 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条日志`
|
||||
}}
|
||||
loading={isLoading}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
// 处理表单查询
|
||||
const handleSearch = (values: any) => {
|
||||
const { timeRange, keyword, level, module, status, ...rest } = values;
|
||||
|
||||
// 构建 where 条件
|
||||
const where: any = {};
|
||||
|
||||
if (level) where.level = level;
|
||||
if (module) where.module = { contains: module };
|
||||
if (status) where.status = status;
|
||||
|
||||
// 处理时间范围
|
||||
if (timeRange && timeRange.length === 2) {
|
||||
where.timestamp = {
|
||||
gte: timeRange[0].startOf('day').toISOString(),
|
||||
lte: timeRange[1].endOf('day').toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
// 处理关键词搜索
|
||||
if (keyword) {
|
||||
where.OR = [
|
||||
{ module: { contains: keyword } },
|
||||
{ action: { contains: keyword } },
|
||||
{ targetName: { contains: keyword } }
|
||||
];
|
||||
}
|
||||
|
||||
// 处理其他条件
|
||||
if (rest.operatorId) where.operatorId = rest.operatorId;
|
||||
if (rest.targetId) where.targetId = rest.targetId;
|
||||
|
||||
console.log('查询参数:', { where });
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: 1, // 重置到第一页
|
||||
where
|
||||
});
|
||||
};
|
||||
|
||||
// 格式化时间显示
|
||||
const formatTime = (timeStr: string) => {
|
||||
return dayjs(timeStr).format('YYYY-MM-DD HH:mm:ss');
|
||||
};
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '操作时间',
|
||||
dataIndex: 'timestamp',
|
||||
key: 'timestamp',
|
||||
render: (text: string) => formatTime(text),
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '级别',
|
||||
dataIndex: 'level',
|
||||
key: 'level',
|
||||
render: (text: string) => (
|
||||
<Tag color={
|
||||
text === 'info' ? 'blue' :
|
||||
text === 'warning' ? 'orange' :
|
||||
text === 'error' ? 'red' : 'green'
|
||||
}>
|
||||
{text.toUpperCase()}
|
||||
</Tag>
|
||||
),
|
||||
filters: [
|
||||
{ text: '信息', value: 'info' },
|
||||
{ text: '警告', value: 'warning' },
|
||||
{ text: '错误', value: 'error' },
|
||||
{ text: '调试', value: 'debug' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '模块',
|
||||
dataIndex: 'module',
|
||||
key: 'module',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
key: 'operator',
|
||||
render: (_, record: ILog) => (
|
||||
record.operator ? record.operator.showname || record.operator.username : '-'
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'IP地址',
|
||||
dataIndex: 'ipAddress',
|
||||
key: 'ipAddress',
|
||||
render: (text: string) => text || '-'
|
||||
},
|
||||
{
|
||||
title: '操作对象',
|
||||
dataIndex: 'targetName',
|
||||
key: 'targetName',
|
||||
render: (text: string) => text || '-'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
render: (text: string) => (
|
||||
<Tag color={text === 'success' ? 'green' : 'red'}>
|
||||
{text === 'success' ? '成功' : '失败'}
|
||||
</Tag>
|
||||
),
|
||||
filters: [
|
||||
{ text: '成功', value: 'success' },
|
||||
{ text: '失败', value: 'failure' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'operation',
|
||||
render: (_, record: ILog) => (
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => showLogDetail(record)}
|
||||
>
|
||||
详情
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
// 显示日志详情的函数
|
||||
const showLogDetail = (record: ILog) => {
|
||||
Modal.info({
|
||||
title: '日志详情',
|
||||
width: 800,
|
||||
content: (
|
||||
<div className="mt-4">
|
||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||
<div><strong>ID:</strong> {record.id}</div>
|
||||
<div><strong>时间:</strong> {formatTime(record.timestamp)}</div>
|
||||
<div><strong>模块:</strong> {record.module}</div>
|
||||
<div><strong>操作:</strong> {record.action}</div>
|
||||
<div><strong>操作人:</strong> {record.operator ? (record.operator.showname || record.operator.username) : '-'}</div>
|
||||
<div><strong>IP地址:</strong> {record.ipAddress || '-'}</div>
|
||||
<div><strong>操作对象:</strong> {record.targetName || '-'}</div>
|
||||
<div><strong>对象类型:</strong> {record.targetType || '-'}</div>
|
||||
<div><strong>状态:</strong> {record.status === 'success' ? '成功' : '失败'}</div>
|
||||
{record.errorMessage && <div><strong>错误信息:</strong> {record.errorMessage}</div>}
|
||||
</div>
|
||||
|
||||
{(record.beforeData || record.afterData) && (
|
||||
<div className="mt-4">
|
||||
<h4 className="mb-2 font-medium">数据变更</h4>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{record.beforeData && (
|
||||
<div>
|
||||
<div className="font-medium mb-1">变更前</div>
|
||||
<pre className="bg-gray-100 p-2 rounded overflow-auto max-h-72">
|
||||
{JSON.stringify(record.beforeData, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{record.afterData && (
|
||||
<div>
|
||||
<div className="font-medium mb-1">变更后</div>
|
||||
<pre className="bg-gray-100 p-2 rounded overflow-auto max-h-72">
|
||||
{JSON.stringify(record.afterData, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{record.details && (
|
||||
<div className="mt-4">
|
||||
<h4 className="mb-2 font-medium">详细信息</h4>
|
||||
<pre className="bg-gray-100 p-2 rounded overflow-auto max-h-72">
|
||||
{JSON.stringify(record.details, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
onOk() {}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Card>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={handleSearch}
|
||||
className="mb-4"
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<Form.Item name="keyword" label="关键词">
|
||||
<Input placeholder="请输入模块/操作/对象名称" allowClear />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="level" label="日志级别">
|
||||
<Select placeholder="请选择级别" allowClear>
|
||||
<Option value="info">信息</Option>
|
||||
<Option value="warning">警告</Option>
|
||||
<Option value="error">错误</Option>
|
||||
<Option value="debug">调试</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="module" label="模块">
|
||||
<Input placeholder="请输入模块" allowClear />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="status" label="状态">
|
||||
<Select placeholder="请选择状态" allowClear>
|
||||
<Option value="success">成功</Option>
|
||||
<Option value="failure">失败</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="timeRange" label="时间范围" className="md:col-span-2">
|
||||
<RangePicker className="w-full" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="operatorId" label="操作人ID">
|
||||
<Input placeholder="请输入操作人ID" allowClear />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="targetId" label="目标ID">
|
||||
<Input placeholder="请输入目标ID" allowClear />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Space>
|
||||
<Button onClick={() => form.resetFields()}>重置</Button>
|
||||
<Button type="primary" htmlType="submit" icon={<SearchOutlined />}>
|
||||
查询
|
||||
</Button>
|
||||
<Button icon={<ReloadOutlined />} onClick={() => refetch()}>
|
||||
刷新
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={data?.items || []}
|
||||
rowKey="id"
|
||||
pagination={{
|
||||
current: queryParams.page,
|
||||
pageSize: queryParams.pageSize,
|
||||
total: data?.total || 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条日志`
|
||||
}}
|
||||
loading={isLoading}
|
||||
onChange={handleTableChange}
|
||||
scroll={{ x: 'max-content' }}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SystemLogPage;
|
|
@ -1,3 +1,4 @@
|
|||
//@ts-nocheck
|
||||
import { getQueryKey } from "@trpc/react-query";
|
||||
import { api } from "../trpc"; // Adjust path as necessary
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
|
@ -5,6 +6,7 @@ import { ObjectType, Staff } from "@nice/common";
|
|||
import { findQueryData } from "../utils";
|
||||
import { CrudOperation, emitDataChange } from "../../event";
|
||||
|
||||
|
||||
export interface CustomField {
|
||||
name: string;
|
||||
label?: string;
|
||||
|
|
Loading…
Reference in New Issue