Files
workspace/code/g3hd/frontend/src/components/auditlog/AuditLogDialog.vue
T
2026-08-08 17:29:51 +08:00

256 lines
4.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<el-dialog
v-model="visible"
title="变更详情"
width="920px"
@close="onClose"
>
<div class="timeline" v-loading="loading">
<div
v-for="(item, index) in logList"
:key="index"
class="timeline-item"
>
<!-- 左侧圆点 + 竖线 -->
<div class="timeline-left">
<div class="dot" :class="dotClass(item.action)"></div>
<div
class="line"
v-if="index < logList.length - 1"
></div>
</div>
<!-- 右侧内容 -->
<div class="timeline-right">
<!-- 摘要行 -->
<div class="summary">
<span class="time">{{ formatTime(item.operateTime) }}</span>
<el-tag :type="tagType(item.action)" size="small">
{{ item.action }}
</el-tag>
<span class="operator">{{ item.operator }}</span>
<span class="table">{{ item.table }}</span>
<span class="row-id">ID: {{ item.rowId }}</span>
</div>
<!-- 展开/折叠 diff -->
<div class="diff-wrapper">
<AuditLogDetail :data="item" />
</div>
</div>
</div>
</div>
<el-empty
v-if="!loading && logList.length === 0"
description="暂无变更记录"
/>
</el-dialog>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import axios from "@/utils/axios";
import AuditLogDetail from "./AuditLogDetail.vue";
const props = defineProps({
modelValue: Boolean,
bizNo: String, // ✅ 业务单据号(loggroupid)
});
const emit = defineEmits(["update:modelValue"]);
const visible = ref(false);
const loading = ref(false);
const logList = ref([]);
watch(
() => props.modelValue,
(val) => {
visible.value = val;
if (val) {
loadLog();
}
}
);
watch(visible, (val) => {
emit("update:modelValue", val);
});
const loadLog = async () => {
if (!props.bizNo) {
ElMessage.warning("bizNo 不能为空");
return;
}
loading.value = true;
try {
const response = await axios.get("/logdetail", {
params: {
bizNo: props.bizNo,
},
});
const result = response.data;
if (!result?.data || result.data.length === 0) {
logList.value = [];
return;
}
const parsedList = [];
for (const raw of result.data) {
try {
const changeData =
typeof raw.changeData === "string"
? JSON.parse(raw.changeData)
: raw.changeData;
changeData.table = changeData.table || raw.tableName;
changeData.operator = changeData.operator || raw.operator;
changeData.operateTime =
changeData.operateTime || raw.operateTime;
parsedList.push(changeData);
} catch (e) {
console.error("changeData 解析失败", e);
}
}
logList.value = parsedList;
} catch (e) {
console.error(e);
ElMessage.error("加载变更日志失败");
} finally {
loading.value = false;
}
};
const onClose = () => {
logList.value = [];
};
const tagType = (action) => {
switch (action) {
case "INSERT":
return "success";
case "UPDATE":
return "primary";
case "DELETE":
return "danger";
default:
return "info";
}
};
const dotClass = (action) => {
switch (action) {
case "INSERT":
return "dot-green";
case "UPDATE":
return "dot-blue";
case "DELETE":
return "dot-red";
default:
return "dot-gray";
}
};
const formatTime = (time) => {
if (!time) return "";
if (typeof time === "string" && time.includes("T")) {
return time.replace("T", " ").split(".")[0];
}
return time;
};
</script>
<style scoped>
.timeline {
padding: 8px 0;
}
.timeline-item {
display: flex;
align-items: flex-start;
}
.timeline-left {
width: 24px;
display: flex;
flex-direction: column;
align-items: center;
margin-right: 12px;
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
margin-top: 6px;
flex-shrink: 0;
}
.dot-green {
background-color: #67c23a;
}
.dot-blue {
background-color: #409eff;
}
.dot-red {
background-color: #f56c6c;
}
.dot-gray {
background-color: #909399;
}
.line {
width: 2px;
flex: 1;
background-color: #e4e7ed;
margin-top: 4px;
min-height: 24px;
}
.timeline-right {
flex: 1;
padding-bottom: 16px;
}
.summary {
display: flex;
align-items: center;
gap: 10px;
font-size: 13px;
color: #606266;
}
.summary .time {
font-weight: bold;
color: #303133;
}
.summary .operator {
color: #409eff;
}
.summary .table {
font-weight: bold;
}
.summary .row-id {
color: #909399;
}
.diff-wrapper {
background-color: #fafafa;
border-radius: 4px;
padding: 8px 12px;
margin-top: 6px;
}
</style>