<template>
  <div v-loading="loading">
    <PageHeader title="报告归档" description="管理巡检任务与工单闭环归档成果">
      <el-button :icon="Refresh" @click="load">刷新</el-button>
      <el-button type="primary" :icon="FolderAdd" @click="archiveDialog = true">生成归档包</el-button>
    </PageHeader>

    <section class="metric-strip">
      <div class="metric-card"><strong>{{ archives.length }}</strong><span>归档包</span><small>任务与工单证据清单</small></div>
      <div class="metric-card"><strong>{{ taskArchiveCount }}</strong><span>任务归档</span><small>巡检任务与飞行成果</small></div>
      <div class="metric-card"><strong>{{ workorderArchiveCount }}</strong><span>工单归档</span><small>处置、证据与复检记录</small></div>
      <div class="metric-card"><strong>{{ readyArchiveCount }}</strong><span>可调阅</span><small>归档清单完整可用</small></div>
    </section>

    <el-card class="workspace-card" shadow="never"><el-table :data="archives" empty-text="暂无归档包" @row-click="inspectArchive">
      <el-table-column prop="archive_no" label="归档编号" min-width="190"><template #default="scope"><strong>{{ scope.row.archive_no }}</strong><small class="cell-subtext entity-id">{{ scope.row.archive_id }}</small></template></el-table-column>
      <el-table-column label="业务类型" width="110"><template #default="scope">{{ scope.row.business_type === 'TASK' ? '巡检任务' : '工单闭环' }}</template></el-table-column>
      <el-table-column prop="business_id" label="业务编号" min-width="170" />
      <el-table-column label="摘要" min-width="190"><template #default="scope"><span class="checksum">{{ scope.row.checksum }}</span></template></el-table-column>
      <el-table-column label="保管期限" width="120"><template #default="scope">{{ scope.row.retention_until }}</template></el-table-column>
      <el-table-column label="状态" width="100"><template #default="scope"><el-tag type="success" effect="plain">{{ scope.row.status === 'READY' ? '可调阅' : scope.row.status }}</el-tag></template></el-table-column>
      <el-table-column label="创建时间" min-width="165"><template #default="scope">{{ formatDate(scope.row.created_at) }}</template></el-table-column>
    </el-table></el-card>

    <el-dialog v-model="archiveDialog" title="生成归档包" width="min(560px, 94vw)"><el-form label-position="top"><el-form-item label="业务类型"><el-radio-group v-model="archiveForm.business_type"><el-radio-button value="TASK">巡检任务</el-radio-button><el-radio-button value="WORKORDER">工单闭环</el-radio-button></el-radio-group></el-form-item><el-form-item label="业务对象"><el-select v-model="archiveForm.business_id" filterable><el-option v-for="item in archiveCandidates" :key="candidateId(item)" :label="candidateLabel(item)" :value="candidateId(item)" /></el-select></el-form-item></el-form><template #footer><el-button @click="archiveDialog = false">取消</el-button><el-button type="primary" :loading="saving" @click="createArchive">生成</el-button></template></el-dialog>

    <el-drawer v-model="detailVisible" title="归档清单" size="min(900px, 96vw)" class="archive-detail-drawer">
      <template v-if="selectedArchive">
        <header class="archive-detail-head">
          <div><span>{{ businessTypeLabel(selectedArchive.business_type) }}</span><strong>{{ selectedArchive.archive_no }}</strong><small>{{ selectedArchive.business_id }}</small></div>
          <el-tag type="success" effect="plain">{{ selectedArchive.status === 'READY' ? '可调阅' : selectedArchive.status }}</el-tag>
        </header>

        <el-descriptions :column="2" border class="archive-summary">
          <el-descriptions-item label="归档时间">{{ formatDate(selectedArchive.created_at) }}</el-descriptions-item>
          <el-descriptions-item label="保管期限">{{ selectedArchive.retention_until }}</el-descriptions-item>
          <el-descriptions-item label="归档人员">{{ selectedArchive.created_by || '-' }}</el-descriptions-item>
          <el-descriptions-item label="清单版本">{{ manifest.schema_version || '-' }}</el-descriptions-item>
        </el-descriptions>

        <template v-if="isWorkorderArchive">
          <section class="archive-business-summary">
            <header><strong>工单概况</strong><span>{{ statusLabel(workorder.status) }}</span></header>
            <dl>
              <div><dt>工单编号</dt><dd>{{ workorder.id || selectedArchive.business_id }}</dd></div>
              <div><dt>当前状态</dt><dd>{{ statusLabel(workorder.status) }}</dd></div>
              <div><dt>处置人员</dt><dd>{{ workorder.assignee || '-' }}</dd></div>
              <div><dt>处置结论</dt><dd>{{ workorder.close_result || '-' }}</dd></div>
              <div><dt>创建时间</dt><dd>{{ formatDate(workorder.created_at) }}</dd></div>
              <div><dt>复核时间</dt><dd>{{ formatDate(workorder.reviewed_at) }}</dd></div>
            </dl>
          </section>

          <section class="archive-count-strip">
            <div><strong>{{ actionRows.length }}</strong><span>处置记录</span></div>
            <div><strong>{{ attachmentRows.length }}</strong><span>证据文件</span></div>
            <div><strong>{{ reviewRows.length }}</strong><span>复核记录</span></div>
            <div><strong>{{ reinspectionRows.length }}</strong><span>复检任务</span></div>
          </section>

          <el-tabs v-model="detailTab" class="archive-detail-tabs">
            <el-tab-pane label="处置时间线" name="timeline">
              <el-empty v-if="!actionRows.length" description="暂无处置记录" :image-size="72" />
              <el-timeline v-else class="archive-timeline">
                <el-timeline-item v-for="action in actionRows" :key="String(action.action_id)" :timestamp="formatDate(action.created_at)" :type="actionTimelineType(action.action_type)" placement="top">
                  <article class="archive-action"><header><strong>{{ actionLabel(action.action_type) }}</strong><el-tag size="small" effect="plain">{{ statusLabel(action.to_status) }}</el-tag></header><p>{{ action.result || action.comment || '状态流转完成' }}</p><small>{{ action.operator_name || '系统' }} · {{ statusLabel(action.from_status) }} → {{ statusLabel(action.to_status) }}</small></article>
                </el-timeline-item>
              </el-timeline>
            </el-tab-pane>
            <el-tab-pane :label="`证据文件 ${attachmentRows.length}`" name="attachments">
              <el-table :data="attachmentRows" empty-text="暂无证据文件">
                <el-table-column prop="file_name" label="文件" min-width="210" />
                <el-table-column label="阶段" width="100"><template #default="scope">{{ phaseLabel(scope.row.phase) }}</template></el-table-column>
                <el-table-column label="摘要" min-width="180"><template #default="scope"><span class="checksum">{{ scope.row.sha256 }}</span></template></el-table-column>
                <el-table-column label="归档时间" width="170"><template #default="scope">{{ formatDate(scope.row.created_at) }}</template></el-table-column>
                <el-table-column label="操作" width="86"><template #default="scope"><el-button link type="primary" :icon="View" @click="previewAttachment(scope.row)">查看</el-button></template></el-table-column>
              </el-table>
            </el-tab-pane>
            <el-tab-pane :label="`复核记录 ${reviewRows.length}`" name="reviews">
              <el-table :data="reviewRows" empty-text="暂无独立复核记录"><el-table-column prop="review_type" label="复核类型" width="120" /><el-table-column prop="decision" label="结论" width="110" /><el-table-column prop="reviewer_name" label="复核人员" min-width="130" /><el-table-column prop="comment" label="意见" min-width="200" /><el-table-column label="时间" width="170"><template #default="scope">{{ formatDate(scope.row.created_at) }}</template></el-table-column></el-table>
            </el-tab-pane>
            <el-tab-pane :label="`复检任务 ${reinspectionRows.length}`" name="reinspections">
              <el-table :data="reinspectionRows" empty-text="暂无复检任务"><el-table-column prop="reinspection_task_id" label="复检任务" min-width="190" /><el-table-column label="状态" width="120"><template #default="scope"><el-tag type="success" effect="plain">{{ reinspectionStatusLabel(scope.row.status) }}</el-tag></template></el-table-column><el-table-column label="创建时间" width="180"><template #default="scope">{{ formatDate(scope.row.created_at) }}</template></el-table-column></el-table>
            </el-tab-pane>
            <el-tab-pane label="技术详情" name="technical">
              <section class="archive-technical-panel"><el-descriptions :column="1" border><el-descriptions-item label="SHA-256"><span class="checksum">{{ selectedArchive.checksum }}</span></el-descriptions-item><el-descriptions-item label="生成时间">{{ formatDate(manifest.generated_at) }}</el-descriptions-item></el-descriptions><pre class="detail-json">{{ prettyManifest(selectedArchive.manifest) }}</pre></section>
            </el-tab-pane>
          </el-tabs>
        </template>

        <template v-else>
          <section class="archive-business-summary">
            <header><strong>巡检任务概况</strong><span>{{ statusLabel(task.status) }}</span></header>
            <dl>
              <div><dt>任务编号</dt><dd>{{ task.id || selectedArchive.business_id }}</dd></div>
              <div><dt>线路</dt><dd>{{ task.line_id || '-' }}</dd></div>
              <div><dt>里程范围</dt><dd>{{ task.mileage_start || '-' }} 至 {{ task.mileage_end || '-' }}</dd></div>
              <div><dt>优先级</dt><dd>{{ priorityLabel(task.priority) }}</dd></div>
              <div><dt>航线</dt><dd>{{ task.route_id || '-' }}</dd></div>
              <div><dt>创建时间</dt><dd>{{ formatDate(task.created_at) }}</dd></div>
            </dl>
          </section>
          <section class="archive-count-strip"><div><strong>{{ missionRows.length }}</strong><span>飞行任务</span></div><div><strong>{{ resourceRows.length }}</strong><span>数据资源</span></div><div><strong>{{ alarmRows.length }}</strong><span>隐患告警</span></div></section>
          <el-tabs v-model="detailTab" class="archive-detail-tabs">
            <el-tab-pane :label="`飞行任务 ${missionRows.length}`" name="missions"><el-table :data="missionRows" empty-text="暂无飞行任务"><el-table-column prop="id" label="任务编号" min-width="180" /><el-table-column prop="vendor_code" label="厂商" width="100" /><el-table-column label="状态" width="110"><template #default="scope">{{ statusLabel(scope.row.status) }}</template></el-table-column><el-table-column prop="progress" label="进度" width="80" /><el-table-column label="完成时间" width="180"><template #default="scope">{{ formatDate(scope.row.completed_at) }}</template></el-table-column></el-table></el-tab-pane>
            <el-tab-pane :label="`数据资源 ${resourceRows.length}`" name="resources"><el-table :data="resourceRows" empty-text="暂无数据资源"><el-table-column prop="id" label="资源编号" min-width="180" /><el-table-column prop="resource_type" label="类型" width="120" /><el-table-column prop="size_bytes" label="大小（字节）" width="120" /><el-table-column label="摘要" min-width="190"><template #default="scope"><span class="checksum">{{ scope.row.sha256 }}</span></template></el-table-column></el-table></el-tab-pane>
            <el-tab-pane :label="`隐患告警 ${alarmRows.length}`" name="alarms"><el-table :data="alarmRows" empty-text="暂无隐患告警"><el-table-column prop="id" label="告警编号" min-width="180" /><el-table-column prop="category" label="类别" min-width="120" /><el-table-column prop="severity" label="等级" width="100" /><el-table-column label="状态" width="110"><template #default="scope">{{ statusLabel(scope.row.status) }}</template></el-table-column><el-table-column label="时间" width="180"><template #default="scope">{{ formatDate(scope.row.created_at) }}</template></el-table-column></el-table></el-tab-pane>
            <el-tab-pane label="技术详情" name="technical"><section class="archive-technical-panel"><el-descriptions :column="1" border><el-descriptions-item label="SHA-256"><span class="checksum">{{ selectedArchive.checksum }}</span></el-descriptions-item><el-descriptions-item label="生成时间">{{ formatDate(manifest.generated_at) }}</el-descriptions-item></el-descriptions><pre class="detail-json">{{ prettyManifest(selectedArchive.manifest) }}</pre></section></el-tab-pane>
          </el-tabs>
        </template>
      </template>
    </el-drawer>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from "vue";
import { ElMessage } from "element-plus";
import { FolderAdd, Refresh, View } from "@element-plus/icons-vue";
import PageHeader from "../../components/common/PageHeader.vue";
import { archivePackages, createArchivePackage, tasks, workorders } from "../../services/api";
import { safeObject, type Row } from "../../types/demo-run";

const archives = ref<Row[]>([]); const taskRows = ref<Row[]>([]); const workorderRows = ref<Row[]>([]); const loading = ref(false); const saving = ref(false); const archiveDialog = ref(false); const detailVisible = ref(false); const detailTab = ref("timeline"); const selectedArchive = ref<Row | null>(null); const archiveForm = reactive<{ business_type: "TASK" | "WORKORDER"; business_id: string }>({ business_type: "WORKORDER", business_id: "" });
const taskArchiveCount = computed(() => archives.value.filter((item) => item.business_type === "TASK").length); const workorderArchiveCount = computed(() => archives.value.filter((item) => item.business_type === "WORKORDER").length); const readyArchiveCount = computed(() => archives.value.filter((item) => item.status === "READY").length); const archiveCandidates = computed(() => archiveForm.business_type === "TASK" ? taskRows.value : workorderRows.value);
const manifest = computed(() => safeObject(selectedArchive.value?.manifest)); const isWorkorderArchive = computed(() => selectedArchive.value?.business_type === "WORKORDER"); const workorder = computed(() => safeObject(manifest.value.workorder)); const task = computed(() => safeObject(manifest.value.task)); const actionRows = computed(() => rows(manifest.value.actions)); const attachmentRows = computed(() => rows(manifest.value.attachments)); const reviewRows = computed(() => rows(manifest.value.reviews)); const reinspectionRows = computed(() => rows(manifest.value.reinspections)); const missionRows = computed(() => rows(manifest.value.missions)); const resourceRows = computed(() => rows(manifest.value.resources)); const alarmRows = computed(() => rows(manifest.value.alarms));
function candidateId(item: Row) { return String(archiveForm.business_type === "TASK" ? item.task_id : item.workorder_id); } function candidateLabel(item: Row) { return archiveForm.business_type === "TASK" ? `${item.task_id} / ${item.line_id}` : `${item.workorder_id} / ${item.scene}`; }
function formatDate(value: unknown) { if (!value) return "-"; const date = new Date(typeof value === "number" ? value : String(value)); return Number.isNaN(date.getTime()) ? "-" : date.toLocaleString("zh-CN", { hour12: false }); }
function prettyManifest(value: unknown) { const parsed = typeof value === "string" ? safeObject(value) : value; return JSON.stringify(parsed, null, 2); }
function rows(value: unknown): Row[] { return Array.isArray(value) ? value.map((item) => safeObject(item)) : []; }
function businessTypeLabel(value: unknown) { return value === "TASK" ? "巡检任务归档" : "工单闭环归档"; }
function statusLabel(value: unknown) { return ({ created: "待派发", dispatched: "已派发", accepted: "已接单", en_route: "前往现场", on_site: "已到场", processing: "处置中", submitted: "待复核", reviewing: "复核中", closed: "已闭环", returned: "已退回", reinspection_required: "待复检", COMPLETED: "已完成", FLYING: "飞行中", READY: "可调阅" } as Record<string, string>)[String(value)] || String(value || "-"); }
function actionLabel(value: unknown) { return ({ ACCEPT: "接收工单", DEPART: "出发前往现场", ARRIVE: "到达现场", START_PROCESSING: "开始处置", EVIDENCE_UPLOADED: "上传现场证据", SUBMIT: "提交复核", START_REVIEW: "开始复核", APPROVE: "复核通过", RETURN: "退回整改", REOPEN: "重新处置" } as Record<string, string>)[String(value)] || String(value || "业务操作"); }
function actionTimelineType(value: unknown): "primary" | "success" | "warning" | "danger" { if (value === "APPROVE") return "success"; if (value === "RETURN") return "danger"; if (value === "EVIDENCE_UPLOADED") return "warning"; return "primary"; }
function phaseLabel(value: unknown) { return ({ BEFORE: "处置前", DURING: "处置中", AFTER: "处置后", REVIEW: "复核" } as Record<string, string>)[String(value)] || String(value || "-"); }
function reinspectionStatusLabel(value: unknown) { return value === "TASK_CREATED" ? "任务已创建" : String(value || "-"); }
function priorityLabel(value: unknown) { return ({ high: "高", normal: "普通", low: "低" } as Record<string, string>)[String(value)] || String(value || "-"); }
function previewAttachment(row: Row) { if (row.id) window.open(`/api/v1/workorder-attachments/${row.id}/content`, "_blank", "noopener,noreferrer"); }
function inspectArchive(row: Row) { selectedArchive.value = row; detailTab.value = row.business_type === "TASK" ? "missions" : "timeline"; detailVisible.value = true; }
async function load() { loading.value = true; try { const [archiveData, taskData, orderData] = await Promise.all([archivePackages(), tasks(), workorders()]); archives.value = archiveData; taskRows.value = taskData; workorderRows.value = orderData; resetCandidate(); } finally { loading.value = false; } }
function resetCandidate() { archiveForm.business_id = archiveCandidates.value.length ? candidateId(archiveCandidates.value[0]) : ""; }
async function createArchive() { if (!archiveForm.business_id) return ElMessage.warning("请选择需要归档的业务对象"); saving.value = true; try { const result = await createArchivePackage(archiveForm.business_type, archiveForm.business_id); ElMessage.success(`归档包已生成：${result.archive_no}`); archiveDialog.value = false; await load(); } finally { saving.value = false; } }
watch(() => archiveForm.business_type, resetCandidate); onMounted(load);
</script>

<style scoped>
.archive-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 18px; padding-bottom: 16px; border-bottom: 1px solid var(--el-border-color-lighter); }
.archive-detail-head > div { display: grid; gap: 4px; min-width: 0; }
.archive-detail-head span, .archive-detail-head small { color: var(--el-text-color-secondary); font-size: 12px; }
.archive-detail-head strong { color: #17365d; font-size: 19px; }
.archive-summary { margin-top: 16px; }
.archive-business-summary { margin-top: 22px; }
.archive-business-summary > header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.archive-business-summary > header strong { color: #17365d; font-size: 15px; }
.archive-business-summary > header span { color: var(--el-color-primary); font-size: 12px; font-weight: 700; }
.archive-business-summary dl { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 0; border-top: 1px solid #e5edf5; border-left: 1px solid #e5edf5; }
.archive-business-summary dl > div { min-width: 0; padding: 10px 12px; border-right: 1px solid #e5edf5; border-bottom: 1px solid #e5edf5; }
.archive-business-summary dt { color: #7a8798; font-size: 11px; }
.archive-business-summary dd { margin: 5px 0 0; overflow-wrap: anywhere; color: #26384d; font-size: 13px; font-weight: 600; }
.archive-count-strip { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin-top: 16px; border: 1px solid #dce4ed; }
.archive-count-strip > div { display: grid; gap: 3px; padding: 11px 14px; border-right: 1px solid #dce4ed; }
.archive-count-strip > div:last-child { border-right: 0; }
.archive-count-strip strong { color: #17365d; font-size: 18px; }
.archive-count-strip span { color: #64748b; font-size: 11px; }
.archive-detail-tabs { margin-top: 18px; }
.archive-timeline { padding: 8px 8px 0 4px; }
.archive-action { padding: 0 0 8px; }
.archive-action header { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.archive-action p { margin: 7px 0 4px; color: #334155; line-height: 1.6; }
.archive-action small { color: #7a8798; }
.archive-technical-panel .detail-json { max-height: 360px; margin-top: 12px; }
@media (max-width: 720px) { .archive-summary :deep(.el-descriptions__body) { overflow-x: auto; } .archive-business-summary dl { grid-template-columns: 1fr 1fr; } .archive-count-strip { grid-template-columns: 1fr 1fr; } .archive-count-strip > div:nth-child(2) { border-right: 0; } }
</style>
