You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
5.5 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<ElDrawer
:model-value="visible"
title="操作日志"
direction="rtl"
size="460px"
append-to-body
@update:model-value="(v: boolean) => emit('update:visible', v)"
>
<template v-if="taskId">
<!-- 任务简要信息 -->
<div v-if="task" class="task-summary">
<ElDescriptions :column="1" size="small" border>
<ElDescriptionsItem label="任务名称">{{ task.task_name }}</ElDescriptionsItem>
<ElDescriptionsItem label="任务编号">
#{{ task.id }}
</ElDescriptionsItem>
</ElDescriptions>
</div>
<!-- 操作记录 -->
<div class="log-section">
<h4 class="section-title">操作记录</h4>
<ElTimeline v-loading="loading" class="log-timeline">
<ElTimelineItem
v-for="log in logList"
:key="log.id"
:timestamp="formatDateTime(log.created_time)"
placement="top"
:type="logType(log.action_type)"
:hollow="log.action_type === 'start_review'"
>
<div class="log-card">
<div class="log-header">
<span class="log-action">{{ log.action_name }}</span>
<ElTag
size="small"
:type="logTagType(log.action_type)"
effect="light"
>
{{ logStatusText(log.action_type) }}
</ElTag>
</div>
<div class="log-operator">
<ElIcon><User /></ElIcon>
{{ log.operator_name || "系统" }}
</div>
<div v-if="log.description" class="log-desc">
{{ log.description }}
</div>
</div>
</ElTimelineItem>
<div v-if="!loading && logList.length === 0" class="empty-logs">
暂无操作记录
</div>
</ElTimeline>
</div>
</template>
</ElDrawer>
</template>
<script setup lang="ts">
import { ref, watch, computed } from "vue";
import { useRoute } from "vue-router";
import { User } from "@element-plus/icons-vue";
import { ReviewTaskAPI } from "@/api/module_report/review";
import type { ReviewTaskItem, ReviewLogItem, ReviewLogActionType } from "@/api/module_report/review";
import type { ReportType } from "@/api/module_report/report";
defineOptions({ name: "LogDrawer" });
const props = defineProps<{
visible: boolean;
taskId?: number | null;
task?: ReviewTaskItem | null;
}>();
const route = useRoute();
/** 报告类型1 能源报告 / 2 光伏报告(由路由参数传入) */
const reportType = computed<ReportType>(() => {
const t = route.query.report_type;
return t === "2" ? 2 : 1;
});
const emit = defineEmits<{
"update:visible": [v: boolean];
}>();
const loading = ref(false);
const logList = ref<ReviewLogItem[]>([]);
watch(
() => [props.visible, props.taskId] as const,
([v, id]) => {
if (v && id) {
loadLogs(id);
}
},
{ immediate: true }
);
async function loadLogs(taskId: number) {
loading.value = true;
logList.value = [];
try {
const resp = await ReviewTaskAPI.listLogs({
report_review_id: taskId,
page_no: 1,
page_size: 100,
});
logList.value = resp?.data?.data?.items ?? [];
} catch (e) {
logList.value = [];
} finally {
loading.value = false;
}
}
function formatDateTime(value?: string): string {
if (!value) return "-";
const d = new Date(value);
if (Number.isNaN(d.getTime())) return value;
const pad = (n: number) => String(n).padStart(2, "0");
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
function logType(t: ReviewLogActionType): "primary" | "success" | "warning" | "danger" {
switch (t) {
case "create_task": return "primary";
case "start_review": return "warning";
case "review_pass": return "success";
case "review_reject": return "danger";
default: return "primary";
}
}
function logTagType(t: ReviewLogActionType): "primary" | "success" | "warning" | "danger" | "info" {
switch (t) {
case "create_task": return "primary";
case "start_review": return "warning";
case "review_pass": return "success";
case "review_reject": return "danger";
default: return "info";
}
}
function logStatusText(t: ReviewLogActionType): string {
switch (t) {
case "create_task": return "已创建";
case "start_review": return "进行中";
case "review_pass": return "已完成";
case "review_reject": return "已驳回";
default: return t;
}
}
</script>
<style lang="scss" scoped>
.task-summary {
margin-bottom: 16px;
}
.log-section {
.section-title {
margin: 0 0 12px;
font-size: 14px;
font-weight: 600;
color: #303133;
padding-left: 8px;
border-left: 3px solid #409eff;
}
}
.log-card {
background: #f9fafb;
border-radius: 6px;
padding: 10px 12px;
border: 1px solid #f0f0f0;
}
.log-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.log-action {
font-weight: 600;
font-size: 14px;
color: #303133;
}
.log-operator {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #909399;
margin-bottom: 4px;
}
.log-desc {
font-size: 12px;
color: #606266;
line-height: 1.6;
background: #fff;
padding: 6px 8px;
border-radius: 4px;
}
.empty-logs {
text-align: center;
color: #909399;
font-size: 13px;
padding: 24px 0;
}
:deep(.el-timeline-item__timestamp) {
color: #909399;
font-size: 12px;
}
</style>