更新AopLogQueryDto中的时间字段类型为OffsetDateTime,优化AopLogRepository中的时间范围查询逻辑,调整前端模拟数据生成器以支持新的时间格式,修复日期选择器样式,优化日志管理页面的用户体验。

This commit is contained in:
Chuck1sn
2025-07-08 14:36:51 +08:00
parent eecc8bedae
commit deece30554
15 changed files with 3258 additions and 3285 deletions

View File

@@ -1,6 +1,8 @@
package com.zl.mjga.dto.aoplog; package com.zl.mjga.dto.aoplog;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -30,10 +32,10 @@ public class AopLogQueryDto {
private String ipAddress; private String ipAddress;
/** 开始时间 */ /** 开始时间 */
private LocalDateTime startTime; private OffsetDateTime startTime;
/** 结束时间 */ /** 结束时间 */
private LocalDateTime endTime; private OffsetDateTime endTime;
/** 最小执行时间(毫秒) */ /** 最小执行时间(毫秒) */
private Long minExecutionTime; private Long minExecutionTime;

View File

@@ -96,13 +96,11 @@ public class AopLogRepository extends AopLogDao {
// 时间范围查询 // 时间范围查询
if (queryDto.getStartTime() != null) { if (queryDto.getStartTime() != null) {
OffsetDateTime startTime = queryDto.getStartTime().atOffset(OffsetDateTime.now().getOffset()); condition = condition.and(AOP_LOG.CREATE_TIME.ge(queryDto.getStartTime()));
condition = condition.and(AOP_LOG.CREATE_TIME.ge(startTime));
} }
if (queryDto.getEndTime() != null) { if (queryDto.getEndTime() != null) {
OffsetDateTime endTime = queryDto.getEndTime().atOffset(OffsetDateTime.now().getOffset()); condition = condition.and(AOP_LOG.CREATE_TIME.le(queryDto.getEndTime()));
condition = condition.and(AOP_LOG.CREATE_TIME.le(endTime));
} }
// 执行时间范围 // 执行时间范围

View File

@@ -3,82 +3,86 @@ import { http, HttpResponse } from "msw";
// 生成AOP日志数据 // 生成AOP日志数据
const generateAopLog = () => ({ const generateAopLog = () => ({
id: faker.number.int({ min: 1, max: 1000 }), id: faker.number.int({ min: 1, max: 1000 }),
className: faker.helpers.arrayElement([ className: faker.helpers.arrayElement([
"com.example.controller.UserController", "com.example.controller.UserController",
"com.example.service.UserService", "com.example.service.UserService",
"com.example.controller.RoleController", "com.example.controller.RoleController",
"com.example.service.RoleService", "com.example.service.RoleService",
"com.example.controller.DepartmentController", "com.example.controller.DepartmentController",
"com.example.service.DepartmentService", "com.example.service.DepartmentService",
]), ]),
methodName: faker.helpers.arrayElement([ methodName: faker.helpers.arrayElement([
"findById", "findById",
"save", "save",
"update", "update",
"delete", "delete",
"findAll", "findAll",
"findByName", "findByName",
"pageQuery", "pageQuery",
]), ]),
methodArgs: JSON.stringify([ methodArgs: JSON.stringify([
{ name: "id", value: faker.number.int({ min: 1, max: 100 }) }, { name: "id", value: faker.number.int({ min: 1, max: 100 }) },
{ name: "name", value: faker.person.fullName() }, { name: "name", value: faker.person.fullName() },
]), ]),
returnValue: JSON.stringify({ returnValue: JSON.stringify({
id: faker.number.int({ min: 1, max: 100 }), id: faker.number.int({ min: 1, max: 100 }),
name: faker.person.fullName(), name: faker.person.fullName(),
success: true, success: true,
}), }),
executionTime: faker.number.int({ min: 10, max: 5000 }), executionTime: faker.number.int({ min: 10, max: 5000 }),
success: faker.datatype.boolean(0.9), // 90%成功率 success: faker.datatype.boolean(0.9), // 90%成功率
errorMessage: faker.helpers.maybe(() => faker.lorem.sentence(), { probability: 0.1 }), errorMessage: faker.helpers.maybe(() => faker.lorem.sentence(), {
userId: faker.number.int({ min: 1, max: 100 }), probability: 0.1,
username: faker.internet.userName(), }),
ipAddress: faker.internet.ip(), userId: faker.number.int({ min: 1, max: 100 }),
userAgent: faker.internet.userAgent(), username: faker.internet.userName(),
curl: `curl -X GET "${faker.internet.url()}" -H "Authorization: Bearer ${faker.string.alphanumeric(32)}"`, ipAddress: faker.internet.ip(),
createTime: faker.date.recent({ days: 30 }).toISOString(), userAgent: faker.internet.userAgent(),
curl: `curl -X GET "${faker.internet.url()}" -H "Authorization: Bearer ${faker.string.alphanumeric(32)}"`,
createTime: faker.date.recent({ days: 30 }).toISOString(),
}); });
export default [ export default [
// 分页查询AOP日志 // 分页查询AOP日志
http.get("/aop-log/page-query", () => { http.get("/aop-log/page-query", () => {
const mockData = { const mockData = {
data: faker.helpers.multiple(generateAopLog, { count: 10 }), data: faker.helpers.multiple(generateAopLog, { count: 10 }),
total: 100, total: 100,
}; };
return HttpResponse.json(mockData); return HttpResponse.json(mockData);
}), }),
// 查询单条日志详情 // 查询单条日志详情
http.get("/aop-log/:id", ({ params }) => { http.get("/aop-log/:id", ({ params }) => {
const id = params.id; const id = params.id;
return HttpResponse.json({ return HttpResponse.json({
...generateAopLog(), ...generateAopLog(),
id: Number(id), id: Number(id),
}); });
}), }),
// 删除单条日志 // 删除单条日志
http.delete("/aop-log/:id", ({ params }) => { http.delete("/aop-log/:id", ({ params }) => {
console.log(`Captured a "DELETE /aop-log/${params.id}" request`); console.log(`Captured a "DELETE /aop-log/${params.id}" request`);
return HttpResponse.json({ success: true }); return HttpResponse.json({ success: true });
}), }),
// 批量删除日志 // 批量删除日志
http.delete("/aop-log/batch", async ({ request }) => { http.delete("/aop-log/batch", async ({ request }) => {
const ids = await request.json(); const ids = await request.json();
console.log(`Captured a "DELETE /aop-log/batch" request with ids: ${ids}`); console.log(`Captured a "DELETE /aop-log/batch" request with ids: ${ids}`);
return HttpResponse.json(ids.length); return HttpResponse.json(ids.length);
}), }),
// 删除指定时间前的日志 // 删除指定时间前的日志
http.delete("/aop-log/before", ({ params }) => { http.delete("/aop-log/before", ({ params }) => {
const { beforeTime } = Object.fromEntries( const { beforeTime } = Object.fromEntries(
new URL(params.request.url).searchParams new URL(params.request.url).searchParams,
); );
console.log(`Captured a "DELETE /aop-log/before" request with time: ${beforeTime}`); console.log(
return HttpResponse.json(faker.number.int({ min: 5, max: 50 })); `Captured a "DELETE /aop-log/before" request with time: ${beforeTime}`,
}), );
]; return HttpResponse.json(faker.number.int({ min: 5, max: 50 }));
}),
];

View File

@@ -3,90 +3,95 @@ import { http, HttpResponse } from "msw";
// 生成模拟的知识库数据 // 生成模拟的知识库数据
const generateLibrary = () => ({ const generateLibrary = () => ({
id: faker.number.int({ min: 1, max: 100 }), id: faker.number.int({ min: 1, max: 100 }),
name: faker.lorem.words(2), name: faker.lorem.words(2),
description: faker.lorem.sentence(), description: faker.lorem.sentence(),
createTime: faker.date.recent().toISOString() createTime: faker.date.recent().toISOString(),
}); });
// 生成模拟的文档数据 // 生成模拟的文档数据
const generateDoc = (libId: number) => ({ const generateDoc = (libId: number) => ({
id: faker.number.int({ min: 1, max: 1000 }), id: faker.number.int({ min: 1, max: 1000 }),
libId, libId,
name: faker.system.fileName(), name: faker.system.fileName(),
identify: faker.string.uuid(), identify: faker.string.uuid(),
path: faker.system.filePath(), path: faker.system.filePath(),
meta: {}, meta: {},
enable: faker.datatype.boolean(), enable: faker.datatype.boolean(),
status: faker.helpers.arrayElement(["SUCCESS", "INDEXING"]), status: faker.helpers.arrayElement(["SUCCESS", "INDEXING"]),
createTime: faker.date.recent().toISOString(), createTime: faker.date.recent().toISOString(),
updateTime: faker.date.recent().toISOString() updateTime: faker.date.recent().toISOString(),
}); });
// 生成模拟的文档段落数据 // 生成模拟的文档段落数据
const generateSegment = (docId: number) => ({ const generateSegment = (docId: number) => ({
id: faker.number.int({ min: 1, max: 10000 }), id: faker.number.int({ min: 1, max: 10000 }),
docId, docId,
embeddingId: faker.string.uuid(), embeddingId: faker.string.uuid(),
content: faker.lorem.paragraphs(), content: faker.lorem.paragraphs(),
tokenUsage: faker.number.int({ min: 10, max: 1000 }) tokenUsage: faker.number.int({ min: 10, max: 1000 }),
}); });
export default [ export default [
// 获取知识库列表 // 获取知识库列表
http.get("/knowledge/libraries", () => { http.get("/knowledge/libraries", () => {
const libraries = faker.helpers.multiple(generateLibrary, { count: 5 }); const libraries = faker.helpers.multiple(generateLibrary, { count: 5 });
return HttpResponse.json(libraries); return HttpResponse.json(libraries);
}), }),
// 获取文档列表 // 获取文档列表
http.get("/knowledge/docs", ({ request }) => { http.get("/knowledge/docs", ({ request }) => {
const url = new URL(request.url); const url = new URL(request.url);
const libraryId = Number(url.searchParams.get("libraryId")); const libraryId = Number(url.searchParams.get("libraryId"));
if (Number.isNaN(libraryId)) {
return new HttpResponse(null, { status: 400 });
}
const docs = faker.helpers.multiple(() => generateDoc(libraryId), { count: 8 });
return HttpResponse.json(docs);
}),
// 获取文档段落 if (Number.isNaN(libraryId)) {
http.get("/knowledge/segments", ({ request }) => { return new HttpResponse(null, { status: 400 });
const url = new URL(request.url); }
const libraryDocId = Number(url.searchParams.get("libraryDocId"));
if (Number.isNaN(libraryDocId)) {
return new HttpResponse(null, { status: 400 });
}
const segments = faker.helpers.multiple(() => generateSegment(libraryDocId), { count: 12 });
return HttpResponse.json(segments);
}),
// 创建/更新知识库 const docs = faker.helpers.multiple(() => generateDoc(libraryId), {
http.post("/knowledge/library", async () => { count: 8,
return HttpResponse.json({ success: true }); });
}), return HttpResponse.json(docs);
}),
// 删除知识库 // 获取文档段落
http.delete("/knowledge/library", () => { http.get("/knowledge/segments", ({ request }) => {
return HttpResponse.json({ success: true }); const url = new URL(request.url);
}), const libraryDocId = Number(url.searchParams.get("libraryDocId"));
// 更新文档 if (Number.isNaN(libraryDocId)) {
http.put("/knowledge/doc", async () => { return new HttpResponse(null, { status: 400 });
return HttpResponse.json({ success: true }); }
}),
// 删除文档 const segments = faker.helpers.multiple(
http.delete("/knowledge/doc", () => { () => generateSegment(libraryDocId),
return HttpResponse.json({ success: true }); { count: 12 },
}), );
return HttpResponse.json(segments);
}),
// 上传文档 // 创建/更新知识库
http.post("/knowledge/doc/upload", async () => { http.post("/knowledge/library", async () => {
return HttpResponse.text("upload-success"); return HttpResponse.json({ success: true });
}), }),
];
// 删除知识库
http.delete("/knowledge/library", () => {
return HttpResponse.json({ success: true });
}),
// 更新文档
http.put("/knowledge/doc", async () => {
return HttpResponse.json({ success: true });
}),
// 删除文档
http.delete("/knowledge/doc", () => {
return HttpResponse.json({ success: true });
}),
// 上传文档
http.post("/knowledge/doc/upload", async () => {
return HttpResponse.text("upload-success");
}),
];

File diff suppressed because it is too large Load Diff

View File

@@ -1,70 +1,69 @@
/* 日期选择器亮色主题 - 与 Flowbite 蓝色主题匹配 */ .dp__theme_light { /* 日期选择器亮色主题 - 与 Flowbite 蓝色主题匹配 */ .dp__theme_light {
/* 基础颜色 */ /* 基础颜色 */
--dp-background-color: #fff; --dp-background-color: #fff;
--dp-text-color: #1f2937; /* 对应 Flowbite 的 gray-800 */ --dp-text-color: #1f2937; /* 对应 Flowbite 的 gray-800 */
/* 主色调 */ /* 主色调 */
--dp-primary-color: #2563eb; /* 对应 Flowbite 的 primary-600 */ --dp-primary-color: #2563eb; /* 对应 Flowbite 的 primary-600 */
--dp-primary-disabled-color: #93c5fd; /* 对应 Flowbite 的 primary-300 */ --dp-primary-disabled-color: #93c5fd; /* 对应 Flowbite 的 primary-300 */
--dp-primary-text-color: #fff; --dp-primary-text-color: #fff;
/* 次要颜色 */ /* 次要颜色 */
--dp-secondary-color: #9ca3af; /* 对应 Flowbite 的 gray-400 */ --dp-secondary-color: #9ca3af; /* 对应 Flowbite 的 gray-400 */
/* 背景颜色 */ /* 背景颜色 */
--dp-background-color: var(--color-gray-50); --dp-background-color: var(--color-gray-50);
/* 边框颜色 */ /* 边框颜色 */
--dp-border-color: var(--color-gray-300); /* 对应 Flowbite 的 gray-200 */ --dp-border-color: var(--color-gray-300); /* 对应 Flowbite 的 gray-200 */
--dp-menu-border-color: #e5e7eb; --dp-menu-border-color: #e5e7eb;
/* 禁用状态 */ /* 禁用状态 */
--dp-disabled-color: #f3f4f6; /* 对应 Flowbite 的 gray-100 */ --dp-disabled-color: #f3f4f6; /* 对应 Flowbite 的 gray-100 */
--dp-disabled-color-text: #9ca3af; /* 对应 Flowbite 的 gray-400 */ --dp-disabled-color-text: #9ca3af; /* 对应 Flowbite 的 gray-400 */
/* 滚动条 */ /* 滚动条 */
--dp-scroll-bar-background: #f3f4f6; /* 对应 Flowbite 的 gray-100 */ --dp-scroll-bar-background: #f3f4f6; /* 对应 Flowbite 的 gray-100 */
--dp-scroll-bar-color: #9ca3af; /* 对应 Flowbite 的 gray-400 */ --dp-scroll-bar-color: #9ca3af; /* 对应 Flowbite 的 gray-400 */
/* 成功状态 */ /* 成功状态 */
--dp-success-color: #10b981; /* 对应 Tailwind 的 emerald-500 */ --dp-success-color: #10b981; /* 对应 Tailwind 的 emerald-500 */
--dp-success-color-disabled: #6ee7b7; /* 对应 Tailwind 的 emerald-300 */ --dp-success-color-disabled: #6ee7b7; /* 对应 Tailwind 的 emerald-300 */
/* 图标颜色 */ /* 图标颜色 */
--dp-icon-color: #6b7280; /* 对应 Flowbite 的 gray-500 */ --dp-icon-color: #6b7280; /* 对应 Flowbite 的 gray-500 */
/* 危险/错误状态 */ /* 危险/错误状态 */
--dp-danger-color: #ef4444; /* 对应 Tailwind 的 red-500 */ --dp-danger-color: #ef4444; /* 对应 Tailwind 的 red-500 */
--dp-marker-color: #ef4444; --dp-marker-color: #ef4444;
/* 提示颜色 */ /* 提示颜色 */
--dp-tooltip-color: #f9fafb; /* 对应 Flowbite 的 gray-50 */ --dp-tooltip-color: #f9fafb; /* 对应 Flowbite 的 gray-50 */
/* 高亮颜色 */ /* 高亮颜色 */
--dp-highlight-color: rgb(37 99 235 / 10%); /* 对应 Flowbite 的 primary-600 透明度 */ --dp-highlight-color: rgb(37 99 235 / 10%); /* 对应 Flowbite 的 primary-600 透明度 */
/* 日期范围相关 */ /* 日期范围相关 */
--dp-range-between-dates-background-color: var(--dp-hover-color, #eff6ff); --dp-range-between-dates-background-color: var(--dp-hover-color, #eff6ff);
--dp-range-between-dates-text-color: var(--dp-hover-text-color, #1f2937); --dp-range-between-dates-text-color: var(--dp-hover-text-color, #1f2937);
--dp-range-between-border-color: var(--dp-hover-color, #eff6ff); --dp-range-between-border-color: var(--dp-hover-color, #eff6ff);
/* 圆角设置 - 匹配项目中的 rounded-lg */ /* 圆角设置 - 匹配项目中的 rounded-lg */
--dp-border-radius: 0.5rem; /* 8px匹配 Tailwind 的 rounded-lg */ --dp-border-radius: 0.5rem; /* 8px匹配 Tailwind 的 rounded-lg */
--dp-cell-border-radius: 0.375rem; /* 6px稍微小一点更美观 */ --dp-cell-border-radius: 0.375rem; /* 6px稍微小一点更美观 */
} }
/* 修复日期文本与图标重叠的问题 */ /* 修复日期文本与图标重叠的问题 */
.dp__input_wrap { .dp__input_wrap {
position: relative; position: relative;
} }
.dp__input { .dp__input {
padding-left: 2rem !important; /* 确保文本不会与图标重叠 */ padding-left: 2rem !important; /* 确保文本不会与图标重叠 */
} }
.dp__input_icon {
position: absolute;
/* left: 0.75rem !important; */
right: auto !important;
}
.dp__input_icon {
position: absolute;
/* left: 0.75rem !important; */
right: auto !important;
}

View File

@@ -19,6 +19,6 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ defineProps<{
success: boolean; success: boolean;
}>(); }>();
</script> </script>

View File

@@ -55,7 +55,7 @@ const props = defineProps<{
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
'change-page': [page: number]; "change-page": [page: number];
}>(); }>();
// 创建一个本地的totalPages引用 // 创建一个本地的totalPages引用
@@ -74,20 +74,20 @@ const {
} = usePagination({ } = usePagination({
initialPage: props.currentPage, initialPage: props.currentPage,
initialTotal: props.total, initialTotal: props.total,
maxVisiblePages: props.maxVisiblePages || 7 // 默认显示7个页码 maxVisiblePages: props.maxVisiblePages || 7, // 默认显示7个页码
}); });
const handlePageChangeClick = async (page: number) => { const handlePageChangeClick = async (page: number) => {
if (page < 1 || page > totalPages.value) return; if (page < 1 || page > totalPages.value) return;
if (props.pageChange) { if (props.pageChange) {
// 如果传入了pageChange函数则调用它 // 如果传入了pageChange函数则调用它
await props.pageChange(page, pageSize.value); await props.pageChange(page, pageSize.value);
} else { } else {
// 否则触发change-page事件 // 否则触发change-page事件
emit('change-page', page); emit("change-page", page);
} }
updatePaginationState({ updatePaginationState({
currentPage: page, currentPage: page,
pageSize: pageSize.value, pageSize: pageSize.value,
@@ -116,7 +116,7 @@ watch(
total: props.total, total: props.total,
}); });
} }
} },
); );
watch( watch(
@@ -125,6 +125,6 @@ watch(
if (newVal !== undefined) { if (newVal !== undefined) {
localTotalPages.value = newVal; localTotalPages.value = newVal;
} }
} },
); );
</script> </script>

View File

@@ -8,94 +8,94 @@ import { ref } from "vue";
* @returns 日志删除相关的状态和方法 * @returns 日志删除相关的状态和方法
*/ */
export function useAopLogDelete() { export function useAopLogDelete() {
const alertStore = useAlertStore(); const alertStore = useAlertStore();
const actionExcStore = useActionExcStore(); const actionExcStore = useActionExcStore();
const loading = ref(false); const loading = ref(false);
/** /**
* 删除单条日志 * 删除单条日志
* @param id 日志ID * @param id 日志ID
*/ */
const deleteLog = async (id: number) => { const deleteLog = async (id: number) => {
try { try {
loading.value = true; loading.value = true;
await client.DELETE("/aop-log/{id}", {
params: {
path: {
id,
},
},
});
alertStore.showAlert({
level: "success",
content: "日志删除成功",
});
actionExcStore.notify(true);
return true;
} finally {
loading.value = false;
}
};
/** await client.DELETE("/aop-log/{id}", {
* 批量删除日志 params: {
* @param ids 日志ID列表 path: {
*/ id,
const batchDeleteLogs = async (ids: number[]) => { },
try { },
loading.value = true; });
const response = await client.DELETE("/aop-log/batch", {
body: ids,
});
alertStore.showAlert({
level: "success",
content: `成功删除 ${response.data || 0} 条日志`,
});
actionExcStore.notify(true);
return true;
} finally {
loading.value = false;
}
};
/** alertStore.showAlert({
* 删除指定时间前的日志 level: "success",
* @param beforeTime 时间点 content: "日志删除成功",
*/ });
const deleteLogsBefore = async (beforeTime: string) => {
try {
loading.value = true;
const response = await client.DELETE("/aop-log/before", {
params: {
query: {
beforeTime,
},
},
});
alertStore.showAlert({
level: "success",
content: `成功删除 ${response.data || 0} 条日志`,
});
actionExcStore.notify(true);
return true;
} finally {
loading.value = false;
}
};
return { actionExcStore.notify(true);
loading, return true;
deleteLog, } finally {
batchDeleteLogs, loading.value = false;
deleteLogsBefore, }
}; };
}
/**
* 批量删除日志
* @param ids 日志ID列表
*/
const batchDeleteLogs = async (ids: number[]) => {
try {
loading.value = true;
const response = await client.DELETE("/aop-log/batch", {
body: ids,
});
alertStore.showAlert({
level: "success",
content: `成功删除 ${response.data || 0} 条日志`,
});
actionExcStore.notify(true);
return true;
} finally {
loading.value = false;
}
};
/**
* 删除指定时间前的日志
* @param beforeTime 时间点
*/
const deleteLogsBefore = async (beforeTime: string) => {
try {
loading.value = true;
const response = await client.DELETE("/aop-log/before", {
params: {
query: {
beforeTime,
},
},
});
alertStore.showAlert({
level: "success",
content: `成功删除 ${response.data || 0} 条日志`,
});
actionExcStore.notify(true);
return true;
} finally {
loading.value = false;
}
};
return {
loading,
deleteLog,
batchDeleteLogs,
deleteLogsBefore,
};
}

View File

@@ -12,120 +12,121 @@ import { ref } from "vue";
* @returns 日志查询相关的状态和方法 * @returns 日志查询相关的状态和方法
*/ */
export function useAopLogQuery() { export function useAopLogQuery() {
const { currentPage, pageSize, total, updatePaginationState } = usePagination(); const { currentPage, pageSize, total, updatePaginationState } =
const { sortBy, handleSort, getSortField } = useSorting(); usePagination();
const { sortBy, handleSort, getSortField } = useSorting();
const logs = ref<components["schemas"]["AopLogRespDto"][]>([]);
const currentLog = ref<components["schemas"]["AopLogRespDto"]>();
const loading = ref(false);
/** const logs = ref<components["schemas"]["AopLogRespDto"][]>([]);
* 分页查询日志列表 const currentLog = ref<components["schemas"]["AopLogRespDto"]>();
* @param params 查询参数 const loading = ref(false);
*/
const fetchLogs = async (params: AopLogQueryParams = {}) => {
try {
loading.value = true;
// 处理日期范围
const queryParams: AopLogQueryParams = { ...params };
const response = await client.GET("/aop-log/page-query", {
params: {
query: {
pageRequestDto: {
page: currentPage.value,
size: pageSize.value,
sortBy: sortBy.value,
},
queryDto: queryParams,
},
},
});
if (response.data) {
logs.value = response.data.data || [];
updatePaginationState({ total: response.data.total || 0 });
}
return logs.value;
} finally {
loading.value = false;
}
};
/** /**
* 获取单条日志详情 * 分页查询日志列表
* @param id 日志ID * @param params 查询参数
*/ */
const fetchLogDetail = async (id: number) => { const fetchLogs = async (params: AopLogQueryParams = {}) => {
try { try {
loading.value = true; loading.value = true;
const response = await client.GET("/aop-log/{id}", {
params: {
path: {
id,
},
},
});
if (response.data) {
currentLog.value = response.data;
}
return currentLog.value;
} finally {
loading.value = false;
}
};
/** // 处理日期范围
* 格式化日期时间 const queryParams: AopLogQueryParams = { ...params };
* @param dateTime 日期时间字符串
*/
const formatDateTime = (dateTime?: string) => {
if (!dateTime) return '';
return dayjs(dateTime).format('YYYY-MM-DD HH:mm:ss');
};
/** const response = await client.GET("/aop-log/page-query", {
* 格式化执行时间 params: {
* @param time 执行时间(毫秒) query: {
*/ pageRequestDto: {
const formatExecutionTime = (time?: number) => { page: currentPage.value,
if (!time) return ''; size: pageSize.value,
if (time < 1000) return `${time}ms`; sortBy: sortBy.value,
return `${(time / 1000).toFixed(2)}s`; },
}; queryDto: queryParams,
},
},
});
/** if (response.data) {
* 格式化JSON字符串 logs.value = response.data.data || [];
* @param jsonString JSON字符串 updatePaginationState({ total: response.data.total || 0 });
*/ }
const formatJson = (jsonString?: string) => {
if (!jsonString) return '';
try {
const obj = JSON.parse(jsonString);
return JSON.stringify(obj, null, 2);
} catch (e) {
return jsonString;
}
};
return { return logs.value;
logs, } finally {
currentLog, loading.value = false;
loading, }
currentPage, };
pageSize,
total, /**
fetchLogs, * 获取单条日志详情
fetchLogDetail, * @param id 日志ID
handleSort, */
getSortField, const fetchLogDetail = async (id: number) => {
formatDateTime, try {
formatExecutionTime, loading.value = true;
formatJson,
}; const response = await client.GET("/aop-log/{id}", {
} params: {
path: {
id,
},
},
});
if (response.data) {
currentLog.value = response.data;
}
return currentLog.value;
} finally {
loading.value = false;
}
};
/**
* 格式化日期时间
* @param dateTime 日期时间字符串
*/
const formatDateTime = (dateTime?: string) => {
if (!dateTime) return "";
return dayjs(dateTime).format("YYYY-MM-DD HH:mm:ss");
};
/**
* 格式化执行时间
* @param time 执行时间(毫秒)
*/
const formatExecutionTime = (time?: number) => {
if (!time) return "";
if (time < 1000) return `${time}ms`;
return `${(time / 1000).toFixed(2)}s`;
};
/**
* 格式化JSON字符串
* @param jsonString JSON字符串
*/
const formatJson = (jsonString?: string) => {
if (!jsonString) return "";
try {
const obj = JSON.parse(jsonString);
return JSON.stringify(obj, null, 2);
} catch (e) {
return jsonString;
}
};
return {
logs,
currentLog,
loading,
currentPage,
pageSize,
total,
fetchLogs,
fetchLogDetail,
handleSort,
getSortField,
formatDateTime,
formatExecutionTime,
formatJson,
};
}

View File

@@ -7,11 +7,11 @@ export interface PaginationState {
} }
export interface UsePaginationOptions { export interface UsePaginationOptions {
initialPage?: number; initialPage?: number;
initialPageSize?: number; initialPageSize?: number;
initialTotal?: number; initialTotal?: number;
maxVisiblePages?: number; maxVisiblePages?: number;
} }
/** /**
* 分页逻辑Composable - 提供分页相关的状态和操作 * 分页逻辑Composable - 提供分页相关的状态和操作

View File

@@ -2,22 +2,22 @@ import type { RouteRecordRaw } from "vue-router";
import { Routes } from "../constants"; import { Routes } from "../constants";
const systemRoutes: RouteRecordRaw[] = [ const systemRoutes: RouteRecordRaw[] = [
{ {
path: Routes.AOPLOGVIEW.path, path: Routes.AOPLOGVIEW.path,
name: Routes.AOPLOGVIEW.name, name: Routes.AOPLOGVIEW.name,
component: () => import("@/views/AopLogManagementPage.vue"), component: () => import("@/views/AopLogManagementPage.vue"),
meta: { meta: {
requiresAuth: true, requiresAuth: true,
}, },
}, },
{ {
path: Routes.AOPLOGDETAILVIEW.path, path: Routes.AOPLOGDETAILVIEW.path,
name: Routes.AOPLOGDETAILVIEW.name, name: Routes.AOPLOGDETAILVIEW.name,
component: () => import("@/views/AopLogDetailPage.vue"), component: () => import("@/views/AopLogDetailPage.vue"),
meta: { meta: {
requiresAuth: true, requiresAuth: true,
}, },
}, },
]; ];
export default systemRoutes; export default systemRoutes;

View File

@@ -6,7 +6,8 @@
<div class="mt-2 sm:mt-0"> <div class="mt-2 sm:mt-0">
<Button variant="secondary" size="sm" @click="navigateBack"> <Button variant="secondary" size="sm" @click="navigateBack">
<template #icon> <template #icon>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18">
</path> </path>
</svg> </svg>
@@ -27,12 +28,12 @@
<div v-if="currentLog.curl" class="p-4 border-b border-gray-200"> <div v-if="currentLog.curl" class="p-4 border-b border-gray-200">
<div class="flex justify-between items-center mb-4"> <div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-medium text-gray-900">CURL</h2> <h2 class="text-lg font-medium text-gray-900">CURL</h2>
<button <button @click="copyCurl" class="text-sm font-medium text-blue-600 hover:text-blue-800 flex items-center">
@click="copyCurl" <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"
class="text-sm font-medium text-blue-600 hover:text-blue-800 flex items-center" xmlns="http://www.w3.org/2000/svg">
> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path> </path>
</svg> </svg>
{{ isCopied ? '已复制' : '复制' }} {{ isCopied ? '已复制' : '复制' }}
</button> </button>
@@ -41,7 +42,7 @@
<pre class="text-sm text-gray-800 overflow-x-auto whitespace-pre-wrap">{{ currentLog.curl }}</pre> <pre class="text-sm text-gray-800 overflow-x-auto whitespace-pre-wrap">{{ currentLog.curl }}</pre>
</div> </div>
</div> </div>
<!-- 基本信息 --> <!-- 基本信息 -->
<div class="p-4 border-b border-gray-200"> <div class="p-4 border-b border-gray-200">
<div class="flex justify-between items-center mb-4"> <div class="flex justify-between items-center mb-4">
@@ -90,12 +91,11 @@
<div class="p-4 border-b border-gray-200"> <div class="p-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900 mb-4">方法参数</h2> <h2 class="text-lg font-medium text-gray-900 mb-4">方法参数</h2>
<div class="bg-gray-50 p-4 rounded-lg"> <div class="bg-gray-50 p-4 rounded-lg">
<pre class="text-sm text-gray-800 overflow-x-auto whitespace-pre-wrap">{{ isMethodArgsExpanded ? formatJson(currentLog.methodArgs) : methodArgsPreview }}</pre> <pre class="text-sm text-gray-800 overflow-x-auto whitespace-pre-wrap">{{ isMethodArgsExpanded ?
formatJson(currentLog.methodArgs) : methodArgsPreview }}</pre>
<div v-if="shouldCollapseMethodArgs" class="mt-2 flex justify-end"> <div v-if="shouldCollapseMethodArgs" class="mt-2 flex justify-end">
<button <button @click="isMethodArgsExpanded = !isMethodArgsExpanded"
@click="isMethodArgsExpanded = !isMethodArgsExpanded" class="text-blue-600 hover:text-blue-800 text-sm font-medium">
class="text-blue-600 hover:text-blue-800 text-sm font-medium"
>
{{ isMethodArgsExpanded ? '折叠' : '展开' }} {{ isMethodArgsExpanded ? '折叠' : '展开' }}
</button> </button>
</div> </div>
@@ -106,12 +106,11 @@
<div class="p-4 border-b border-gray-200"> <div class="p-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900 mb-4">返回值</h2> <h2 class="text-lg font-medium text-gray-900 mb-4">返回值</h2>
<div class="bg-gray-50 p-4 rounded-lg"> <div class="bg-gray-50 p-4 rounded-lg">
<pre class="text-sm text-gray-800 overflow-x-auto whitespace-pre-wrap">{{ isReturnValueExpanded ? formatJson(currentLog.returnValue) : returnValuePreview }}</pre> <pre class="text-sm text-gray-800 overflow-x-auto whitespace-pre-wrap">{{ isReturnValueExpanded ?
formatJson(currentLog.returnValue) : returnValuePreview }}</pre>
<div v-if="shouldCollapseReturnValue" class="mt-2 flex justify-end"> <div v-if="shouldCollapseReturnValue" class="mt-2 flex justify-end">
<button <button @click="isReturnValueExpanded = !isReturnValueExpanded"
@click="isReturnValueExpanded = !isReturnValueExpanded" class="text-blue-600 hover:text-blue-800 text-sm font-medium">
class="text-blue-600 hover:text-blue-800 text-sm font-medium"
>
{{ isReturnValueExpanded ? '折叠' : '展开' }} {{ isReturnValueExpanded ? '折叠' : '展开' }}
</button> </button>
</div> </div>
@@ -122,12 +121,11 @@
<div v-if="currentLog.errorMessage" class="p-4 border-b border-gray-200"> <div v-if="currentLog.errorMessage" class="p-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900 mb-4">错误信息</h2> <h2 class="text-lg font-medium text-gray-900 mb-4">错误信息</h2>
<div class="bg-red-50 p-4 rounded-lg"> <div class="bg-red-50 p-4 rounded-lg">
<pre class="text-sm text-red-800 overflow-x-auto whitespace-pre-wrap">{{ isErrorMessageExpanded ? currentLog.errorMessage : errorMessagePreview }}</pre> <pre class="text-sm text-red-800 overflow-x-auto whitespace-pre-wrap">{{ isErrorMessageExpanded ?
currentLog.errorMessage : errorMessagePreview }}</pre>
<div v-if="shouldCollapseErrorMessage" class="mt-2 flex justify-end"> <div v-if="shouldCollapseErrorMessage" class="mt-2 flex justify-end">
<button <button @click="isErrorMessageExpanded = !isErrorMessageExpanded"
@click="isErrorMessageExpanded = !isErrorMessageExpanded" class="text-blue-600 hover:text-blue-800 text-sm font-medium">
class="text-blue-600 hover:text-blue-800 text-sm font-medium"
>
{{ isErrorMessageExpanded ? '折叠' : '展开' }} {{ isErrorMessageExpanded ? '折叠' : '展开' }}
</button> </button>
</div> </div>
@@ -138,7 +136,8 @@
<div class="p-4 bg-gray-50 rounded-b-lg flex justify-end"> <div class="p-4 bg-gray-50 rounded-b-lg flex justify-end">
<Button variant="danger" @click="handleDeleteClick"> <Button variant="danger" @click="handleDeleteClick">
<template #icon> <template #icon>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"> d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16">
</path> </path>
@@ -156,7 +155,7 @@
</div> </div>
<!-- 删除确认对话框 --> <!-- 删除确认对话框 -->
<ConfirmationDialog id="delete-log-modal" title="删除日志" content="确定要删除此日志吗?此操作不可撤销。" <ConfirmationDialog id="delete-log-modal" title="删除日志" content="确定要删除此日志吗?此操作不可撤销。"
:closeModal="() => deleteLogModal?.hide()" :onSubmit="confirmDelete" /> :closeModal="() => deleteLogModal?.hide()" :onSubmit="confirmDelete" />
</div> </div>
</template> </template>
@@ -173,7 +172,6 @@ import { Button } from "@/components/ui";
import { useAopLogDelete } from "@/composables/aop/useAopLogDelete"; import { useAopLogDelete } from "@/composables/aop/useAopLogDelete";
import { useAopLogQuery } from "@/composables/aop/useAopLogQuery"; import { useAopLogQuery } from "@/composables/aop/useAopLogQuery";
import { useErrorHandling } from "@/composables/common/useErrorHandling";
import { Routes } from "@/router/constants"; import { Routes } from "@/router/constants";
// 路由 // 路由
@@ -181,17 +179,14 @@ const route = useRoute();
const router = useRouter(); const router = useRouter();
const logId = Number(route.params.id); const logId = Number(route.params.id);
// 获取错误处理
const { handleError } = useErrorHandling();
// 获取日志查询和删除的composables // 获取日志查询和删除的composables
const { const {
currentLog, currentLog,
loading, loading,
fetchLogDetail, fetchLogDetail,
formatDateTime, formatDateTime,
formatExecutionTime, formatExecutionTime,
formatJson, formatJson,
} = useAopLogQuery(); } = useAopLogQuery();
const { deleteLog } = useAopLogDelete(); const { deleteLog } = useAopLogDelete();
@@ -211,90 +206,83 @@ const isCopied = ref(false);
const previewLength = 300; const previewLength = 300;
const methodArgsPreview = computed(() => { const methodArgsPreview = computed(() => {
const content = formatJson(currentLog.value?.methodArgs); const content = formatJson(currentLog.value?.methodArgs);
return content && content.length > previewLength return content && content.length > previewLength
? `${content.substring(0, previewLength)}...` ? `${content.substring(0, previewLength)}...`
: content; : content;
}); });
const returnValuePreview = computed(() => { const returnValuePreview = computed(() => {
const content = formatJson(currentLog.value?.returnValue); const content = formatJson(currentLog.value?.returnValue);
return content && content.length > previewLength return content && content.length > previewLength
? `${content.substring(0, previewLength)}...` ? `${content.substring(0, previewLength)}...`
: content; : content;
}); });
const errorMessagePreview = computed(() => { const errorMessagePreview = computed(() => {
const content = currentLog.value?.errorMessage; const content = currentLog.value?.errorMessage;
return content && content.length > previewLength return content && content.length > previewLength
? `${content.substring(0, previewLength)}...` ? `${content.substring(0, previewLength)}...`
: content; : content;
}); });
// 判断是否需要折叠 // 判断是否需要折叠
const shouldCollapseMethodArgs = computed(() => { const shouldCollapseMethodArgs = computed(() => {
const content = formatJson(currentLog.value?.methodArgs); const content = formatJson(currentLog.value?.methodArgs);
return content && content.length > previewLength; return content && content.length > previewLength;
}); });
const shouldCollapseReturnValue = computed(() => { const shouldCollapseReturnValue = computed(() => {
const content = formatJson(currentLog.value?.returnValue); const content = formatJson(currentLog.value?.returnValue);
return content && content.length > previewLength; return content && content.length > previewLength;
}); });
const shouldCollapseErrorMessage = computed(() => { const shouldCollapseErrorMessage = computed(() => {
const content = currentLog.value?.errorMessage; const content = currentLog.value?.errorMessage;
return content && content.length > previewLength; return content && content.length > previewLength;
}); });
// 复制CURL命令 // 复制CURL命令
const copyCurl = () => { const copyCurl = () => {
if (currentLog.value?.curl) { if (currentLog.value?.curl) {
navigator.clipboard.writeText(currentLog.value.curl); navigator.clipboard.writeText(currentLog.value.curl);
isCopied.value = true; isCopied.value = true;
setTimeout(() => { setTimeout(() => {
isCopied.value = false; isCopied.value = false;
}, 2000); }, 2000);
} }
}; };
// 返回日志列表 // 返回日志列表
const navigateBack = () => { const navigateBack = () => {
router.push(Routes.AOPLOGVIEW.fullPath()); router.push(Routes.AOPLOGVIEW.fullPath());
}; };
// 处理删除点击 // 处理删除点击
const handleDeleteClick = () => { const handleDeleteClick = () => {
deleteLogModal.value?.show(); deleteLogModal.value?.show();
}; };
// 确认删除 // 确认删除
const confirmDelete = async () => { const confirmDelete = async () => {
try { if (currentLog.value?.id) {
if (currentLog.value?.id) { await deleteLog(currentLog.value.id);
await deleteLog(currentLog.value.id); navigateBack();
navigateBack(); }
}
} catch (error) {
handleError(error);
}
}; };
onMounted(async () => { onMounted(async () => {
try { // 初始化Flowbite
// 初始化Flowbite initFlowbite();
initFlowbite();
// 初始化模态框 // 初始化模态框
const $deleteModalElement = document.querySelector<HTMLElement>("#delete-log-modal"); const $deleteModalElement =
if ($deleteModalElement) { document.querySelector<HTMLElement>("#delete-log-modal");
deleteLogModal.value = new Modal($deleteModalElement); if ($deleteModalElement) {
} deleteLogModal.value = new Modal($deleteModalElement);
}
// 加载日志详情 // 加载日志详情
await fetchLogDetail(logId); await fetchLogDetail(logId);
} catch (error) {
handleError(error);
}
}); });
</script> </script>

View File

@@ -92,7 +92,7 @@
<!-- 移动端卡片列表 --> <!-- 移动端卡片列表 -->
<div class="md:hidden mt-4"> <div class="md:hidden mt-4">
<MobileCardListWithCheckbox :items="logs" v-model="selectedLogs" idField="id"> <MobileCardListWithCheckbox :items="logs" v-model="selectedLogs" idField="id">
<template #item="{ item }"> <template v-for="item in logs" :key="item.id">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex justify-between items-start"> <div class="flex justify-between items-start">
<div class="font-medium text-gray-900 truncate max-w-[200px]" :title="item.className"> <div class="font-medium text-gray-900 truncate max-w-[200px]" :title="item.className">
@@ -109,10 +109,6 @@
<div class="text-gray-500">执行时间</div> <div class="text-gray-500">执行时间</div>
<div>{{ formatExecutionTime(item.executionTime) }}</div> <div>{{ formatExecutionTime(item.executionTime) }}</div>
</div> </div>
<div>
<div class="text-gray-500">用户</div>
<div>{{ item.username || '-' }}</div>
</div>
<div> <div>
<div class="text-gray-500">创建时间</div> <div class="text-gray-500">创建时间</div>
<div>{{ formatDateTime(item.createTime) }}</div> <div>{{ formatDateTime(item.createTime) }}</div>
@@ -172,8 +168,8 @@
删除此日期之前的所有日志 删除此日期之前的所有日志
</label> </label>
<div class="datepicker-container"> <div class="datepicker-container">
<VueDatePicker v-model="clearBeforeDate" locale="zh-CN" format="yyyy-MM-dd" :enable-time-picker="false" <VueDatePicker v-model="clearBeforeDate" locale="zh-CN" format="yyyy/MM/dd HH:mm:ss"
:auto-apply="true" class="filter-datepicker" teleport="body" /> :enable-time-picker="false" :auto-apply="true" class="filter-datepicker" teleport="body" />
</div> </div>
</div> </div>
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">
@@ -214,6 +210,7 @@ import { useErrorHandling } from "@/composables/common/useErrorHandling";
import { useActionExcStore } from "@/composables/store/useActionExcStore"; import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { Routes } from "@/router/constants"; import { Routes } from "@/router/constants";
import type { AopLogQueryParams } from "@/types/AlertTypes"; import type { AopLogQueryParams } from "@/types/AlertTypes";
import { formatDate } from "@/utils";
// 路由 // 路由
const router = useRouter(); const router = useRouter();
@@ -223,16 +220,14 @@ const { handleError } = useErrorHandling();
// 获取日志查询和删除的composables // 获取日志查询和删除的composables
const { const {
logs, logs,
currentPage, currentPage,
pageSize, pageSize,
total, total,
loading, loading,
fetchLogs, fetchLogs,
formatDateTime, formatDateTime,
formatExecutionTime, formatExecutionTime,
handleSort,
getSortField,
} = useAopLogQuery(); } = useAopLogQuery();
const { deleteLog, batchDeleteLogs, deleteLogsBefore } = useAopLogDelete(); const { deleteLog, batchDeleteLogs, deleteLogsBefore } = useAopLogDelete();
@@ -253,230 +248,212 @@ const currentLogToDelete = ref<components["schemas"]["AopLogRespDto"]>();
// 筛选配置 // 筛选配置
const filterConfig = [ const filterConfig = [
{ {
type: "input", type: "input",
name: "className", name: "className",
placeholder: "类名", placeholder: "类名",
}, },
{ {
type: "input", type: "input",
name: "methodName", name: "methodName",
placeholder: "方法名", placeholder: "方法名",
}, },
{ {
type: "select", type: "select",
name: "success", name: "success",
placeholder: "状态", placeholder: "状态",
options: [ options: [
{ value: "", label: "全部" }, { value: "", label: "全部" },
{ value: "true", label: "成功" }, { value: "true", label: "成功" },
{ value: "false", label: "失败" }, { value: "false", label: "失败" },
], ],
}, },
{ {
type: "input", type: "input",
name: "username", name: "username",
placeholder: "用户名", placeholder: "用户名",
}, },
{ {
type: "date-range", type: "date-range",
name: "dateRange", name: "dateRange",
}, },
] as FilterItem[]; ] as FilterItem[];
// 筛选值 // 筛选值
const filterValues = reactive<{ const filterValues = reactive<{
className: string; className: string;
methodName: string; methodName: string;
success: string; success: string;
username: string; username: string;
dateRange: Date[]; dateRange: Date[];
}>({ }>({
className: "", className: "",
methodName: "", methodName: "",
success: "", success: "",
username: "", username: "",
dateRange: [], dateRange: [],
}); });
// 表格列配置 // 表格列配置
const columns = [ const columns = [
{ title: "类名", field: "className", sortable: true }, { title: "类名", field: "className", sortable: true },
{ title: "方法名", field: "methodName", sortable: true }, { title: "方法名", field: "methodName", sortable: true },
{ title: "执行时间", field: "executionTime", sortable: true }, { title: "执行时间", field: "executionTime", sortable: true },
{ title: "状态", field: "success" }, { title: "状态", field: "success" },
{ title: "用户", field: "username" }, { title: "用户", field: "username" },
{ title: "创建时间", field: "createTime", sortable: true }, { title: "创建时间", field: "createTime", sortable: true },
{ title: "操作", field: "actions" }, { title: "操作", field: "actions" },
]; ];
// 更新筛选值 // 更新筛选值
const updateFilterValues = ( const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined> values: Record<string, string | number | boolean | Date[] | undefined>,
) => { ) => {
if (values.className !== undefined) { if (values.className !== undefined) {
filterValues.className = values.className as string; filterValues.className = values.className as string;
} }
if (values.methodName !== undefined) { if (values.methodName !== undefined) {
filterValues.methodName = values.methodName as string; filterValues.methodName = values.methodName as string;
} }
if (values.success !== undefined) { if (values.success !== undefined) {
filterValues.success = values.success as string; filterValues.success = values.success as string;
} }
if (values.username !== undefined) { if (values.username !== undefined) {
filterValues.username = values.username as string; filterValues.username = values.username as string;
} }
if (values.dateRange !== undefined) { if (values.dateRange !== undefined) {
filterValues.dateRange = values.dateRange as Date[]; filterValues.dateRange = values.dateRange as Date[];
} }
}; };
// 处理搜索 // 处理搜索
const handleSearch = async () => { const handleSearch = async () => {
try { const params: AopLogQueryParams = {
const params: AopLogQueryParams = { className: filterValues.className || undefined,
className: filterValues.className || undefined, methodName: filterValues.methodName || undefined,
methodName: filterValues.methodName || undefined, success: filterValues.success ? filterValues.success === "true" : undefined,
success: filterValues.success ? filterValues.success === "true" : undefined, };
username: filterValues.username || undefined,
};
// 处理日期范围 // 处理日期范围
if (filterValues.dateRange && filterValues.dateRange.length === 2) { if (filterValues.dateRange && filterValues.dateRange.length === 2) {
params.startTime = filterValues.dateRange[0].toISOString(); params.startTime = formatDate(filterValues.dateRange[0]);
params.endTime = filterValues.dateRange[1].toISOString(); params.endTime = formatDate(filterValues.dateRange[1]);
} }
await fetchLogs(params); await fetchLogs(params);
selectedLogs.value = []; selectedLogs.value = [];
} catch (error) {
handleError(error);
}
}; };
// 处理页码变化 // 处理页码变化
const handlePageChange = async (page: number) => { const handlePageChange = async (page: number) => {
try { currentPage.value = page;
currentPage.value = page; await handleSearch();
await handleSearch();
} catch (error) {
handleError(error);
}
}; };
// 处理查看详情 // 处理查看详情
const handleViewDetail = (log: components["schemas"]["AopLogRespDto"]) => { const handleViewDetail = (log: components["schemas"]["AopLogRespDto"]) => {
if (log.id) { if (log.id) {
router.push(Routes.AOPLOGDETAILVIEW.withParams({ id: log.id })); router.push(Routes.AOPLOGDETAILVIEW.withParams({ id: log.id }));
} }
}; };
// 处理删除点击 // 处理删除点击
const handleDeleteClick = (log: components["schemas"]["AopLogRespDto"]) => { const handleDeleteClick = (log: components["schemas"]["AopLogRespDto"]) => {
currentLogToDelete.value = log; currentLogToDelete.value = log;
deleteLogModal.value?.show(); deleteLogModal.value?.show();
}; };
// 确认删除 // 确认删除
const confirmDelete = async () => { const confirmDelete = async () => {
try { if (currentLogToDelete.value?.id) {
if (currentLogToDelete.value?.id) { await deleteLog(currentLogToDelete.value.id);
await deleteLog(currentLogToDelete.value.id); await handleSearch();
await handleSearch(); deleteLogModal.value?.hide();
deleteLogModal.value?.hide(); }
}
} catch (error) {
handleError(error);
}
}; };
// 处理批量删除点击 // 处理批量删除点击
const handleBatchDeleteClick = () => { const handleBatchDeleteClick = () => {
if (selectedLogs.value.length > 0) { if (selectedLogs.value.length > 0) {
batchDeleteLogsModal.value?.show(); batchDeleteLogsModal.value?.show();
} }
}; };
// 确认批量删除 // 确认批量删除
const confirmBatchDelete = async () => { const confirmBatchDelete = async () => {
try { if (selectedLogs.value.length > 0) {
if (selectedLogs.value.length > 0) { await batchDeleteLogs(selectedLogs.value);
await batchDeleteLogs(selectedLogs.value); await handleSearch();
await handleSearch(); batchDeleteLogsModal.value?.hide();
batchDeleteLogsModal.value?.hide(); }
}
} catch (error) {
handleError(error);
}
}; };
// 处理清理历史日志点击 // 处理清理历史日志点击
const handleClearBeforeClick = () => { const handleClearBeforeClick = () => {
clearBeforeModal.value?.show(); clearBeforeModal.value?.show();
}; };
// 关闭清理历史日志对话框 // 关闭清理历史日志对话框
const closeClearBeforeModal = () => { const closeClearBeforeModal = () => {
clearBeforeModal.value?.hide(); clearBeforeModal.value?.hide();
}; };
// 确认清理历史日志 // 确认清理历史日志
const confirmClearBefore = async () => { const confirmClearBefore = async () => {
try { if (clearBeforeDate.value) {
if (clearBeforeDate.value) { const dateString = formatDate(clearBeforeDate.value);
const dateString = clearBeforeDate.value.toISOString().split("T")[0]; await deleteLogsBefore(dateString!);
await deleteLogsBefore(dateString); await handleSearch();
await handleSearch(); clearBeforeModal.value?.hide();
clearBeforeModal.value?.hide(); }
}
} catch (error) {
handleError(error);
}
}; };
// 处理全选 // 处理全选
const handleSelectAll = (selected: boolean) => { const handleSelectAll = (selected: boolean) => {
if (selected) { if (selected) {
selectedLogs.value = logs.value.map(log => log.id).filter((id): id is number => id !== undefined); selectedLogs.value = logs.value
} else { .map((log) => log.id)
selectedLogs.value = []; .filter((id): id is number => id !== undefined);
} } else {
selectedLogs.value = [];
}
}; };
// 初始化 // 初始化
const actionExcStore = useActionExcStore(); const actionExcStore = useActionExcStore();
onMounted(async () => { onMounted(async () => {
try { // 初始化Flowbite
// 初始化Flowbite initFlowbite();
initFlowbite();
// 初始化模态框 // 初始化模态框
const $deleteModalElement = document.querySelector<HTMLElement>("#delete-log-modal"); const $deleteModalElement =
const $batchDeleteModalElement = document.querySelector<HTMLElement>("#batch-delete-logs-modal"); document.querySelector<HTMLElement>("#delete-log-modal");
const $clearBeforeModalElement = document.querySelector<HTMLElement>("#clear-before-modal"); const $batchDeleteModalElement = document.querySelector<HTMLElement>(
"#batch-delete-logs-modal",
);
const $clearBeforeModalElement = document.querySelector<HTMLElement>(
"#clear-before-modal",
);
if ($deleteModalElement) { if ($deleteModalElement) {
deleteLogModal.value = new Modal($deleteModalElement); deleteLogModal.value = new Modal($deleteModalElement);
} }
if ($batchDeleteModalElement) { if ($batchDeleteModalElement) {
batchDeleteLogsModal.value = new Modal($batchDeleteModalElement); batchDeleteLogsModal.value = new Modal($batchDeleteModalElement);
} }
if ($clearBeforeModalElement) { if ($clearBeforeModalElement) {
clearBeforeModal.value = new Modal($clearBeforeModalElement); clearBeforeModal.value = new Modal($clearBeforeModalElement);
} }
// 加载日志数据 // 加载日志数据
await fetchLogs(); await fetchLogs();
// 设置刷新回调 // 设置刷新回调
actionExcStore.setCallback((result) => { actionExcStore.setCallback((result) => {
if (result) { if (result) {
handleSearch(); handleSearch();
} }
}); });
} catch (error) {
handleError(error);
}
}); });
</script> </script>

View File

@@ -164,7 +164,6 @@ import type { components } from "@/api/types/schema";
import PromotionBanner from "@/components/common/PromotionBanner.vue"; import PromotionBanner from "@/components/common/PromotionBanner.vue";
import { PlusIcon } from "@/components/icons"; import { PlusIcon } from "@/components/icons";
import Breadcrumbs from "@/components/layout/Breadcrumbs.vue"; import Breadcrumbs from "@/components/layout/Breadcrumbs.vue";
import ConfirmationDialog from "@/components/modals/ConfirmationDialog.vue";
import UserDeleteModal from "@/components/modals/ConfirmationDialog.vue"; import UserDeleteModal from "@/components/modals/ConfirmationDialog.vue";
import UserFormDialog from "@/components/modals/UserFormDialog.vue"; import UserFormDialog from "@/components/modals/UserFormDialog.vue";
import TableButton from "@/components/tables/TableButton.vue"; import TableButton from "@/components/tables/TableButton.vue";