feat(M08-B): 补联调阻断摘要

This commit is contained in:
Codex
2026-07-05 16:02:51 +08:00
parent 8e4264589d
commit a44864309c
7 changed files with 92 additions and 9 deletions
@@ -58,6 +58,21 @@
show-icon
:closable="false"
/>
<section v-if="blockedItems.length" class="handoff-blockers">
<header>
<div>
<h3>阻断摘要</h3>
<span>{{ blockedItems.length }} 个现场阻断项需要外部处理</span>
</div>
<el-button :icon="Copy" @click="copyBlockedSummary">复制摘要</el-button>
</header>
<ul>
<li v-for="item in blockedItems" :key="item.id">
<strong>{{ item.category }} · {{ item.title }}</strong>
<span>{{ draft[item.id].owner || '未分派' }} / {{ draft[item.id].note || item.expected }}</span>
</li>
</ul>
</section>
<el-table :data="filteredChecklist" class="data-table" row-key="id">
<el-table-column prop="category" label="类别" width="120" />
@@ -111,7 +126,8 @@
<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
import { Check, Download, ListChecks, RotateCcw } from '@lucide/vue';
import { Check, Copy, Download, ListChecks, RotateCcw } from '@lucide/vue';
import { ElMessage } from 'element-plus';
type FieldStatus = 'PENDING' | 'PASS' | 'BLOCKED' | 'SKIP';
@@ -206,6 +222,7 @@ const filteredChecklist = computed(() => checklist.filter((item) => {
const passedCount = computed(() => checklist.filter((item) => draft[item.id].status === 'PASS').length);
const blockedCount = computed(() => checklist.filter((item) => draft[item.id].status === 'BLOCKED').length);
const pendingCount = computed(() => checklist.filter((item) => draft[item.id].status === 'PENDING').length);
const blockedItems = computed(() => checklist.filter((item) => draft[item.id].status === 'BLOCKED'));
function createInitialDraft() {
const saved = readSavedDraft();
@@ -243,6 +260,19 @@ function applyBulkMark() {
persistDraft();
}
async function copyBlockedSummary() {
const content = blockedItems.value.map((item) => {
const itemDraft = draft[item.id];
return `[${item.category}] ${item.title} - ${itemDraft.owner || '未分派'} - ${itemDraft.note || item.expected}`;
}).join('\n');
try {
await navigator.clipboard.writeText(content);
ElMessage.success('阻断摘要已复制');
} catch {
ElMessage.warning('当前浏览器不允许自动复制,请使用导出 CSV 留存');
}
}
function exportChecklist() {
downloadCsv(`cleaning-field-handoff-${Date.now()}.csv`, [
['ID', '类别', '检查项', '期望', '状态', '负责人', '结果记录', '证据'],
+40
View File
@@ -535,6 +535,46 @@ textarea {
margin-bottom: 0;
}
.handoff-blockers {
border: 1px solid #f0c7c7;
border-radius: 8px;
overflow: hidden;
background: #fff7f7;
}
.handoff-blockers header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
border-bottom: 1px solid #f0c7c7;
}
.handoff-blockers h3 {
margin: 0;
font-size: 14px;
}
.handoff-blockers header span,
.handoff-blockers li span {
color: #7f4b4b;
font-size: 12px;
}
.handoff-blockers ul {
display: grid;
gap: 8px;
margin: 0;
padding: 10px 12px;
list-style: none;
}
.handoff-blockers li {
display: grid;
gap: 3px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));