From 3471290659516cf21db3211a9053daff5f283e03 Mon Sep 17 00:00:00 2001
From: zhuguifei <312353457@qq.com>
Date: 星期五, 20 三月 2026 15:50:18 +0800
Subject: [PATCH] feat: 基础数据仪器管理、判定依据、判定依据明细

---
 ruoyi-plus-soybean/src/views/report/demo/index.vue |  879 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 879 insertions(+), 0 deletions(-)

diff --git a/ruoyi-plus-soybean/src/views/report/demo/index.vue b/ruoyi-plus-soybean/src/views/report/demo/index.vue
new file mode 100755
index 0000000..7def393
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/report/demo/index.vue
@@ -0,0 +1,879 @@
+<script setup lang="ts">
+import { computed, ref } from 'vue';
+
+defineOptions({
+  name: 'SilkStorageOutputReport'
+});
+
+type SegmentKey = 'boxMark' | 'tiaoheMark' | 'heBarcode' | 'tiaoheBarcode' | 'syncResult';
+type BottomKey =
+  | 'boxPack'
+  | 'tiaoPack'
+  | 'hePack'
+  | 'appearance'
+  | 'hotStamp'
+  | 'endFade'
+  | 'impurityRate'
+  | 'moistureRate'
+  | 'length'
+  | 'composite';
+
+const zoom = ref(1);
+const activeSegment = ref<SegmentKey>('syncResult');
+const activeBottom = ref<BottomKey>('composite');
+
+const segmentOptions: { key: SegmentKey; label: string; tone: 'warning' | 'info' | 'success' }[] = [
+  { key: 'boxMark', label: '绠辨爣璇�', tone: 'warning' },
+  { key: 'tiaoheMark', label: '鏉$洅鏍囪瘑', tone: 'warning' },
+  { key: 'heBarcode', label: '鐩掓潯鐮�', tone: 'success' },
+  { key: 'tiaoheBarcode', label: '鏉$洅鐮�', tone: 'success' },
+  { key: 'syncResult', label: '鍚屾鐜板満缁撴灉', tone: 'info' }
+];
+
+const bottomOptions: { key: BottomKey; label: string; tone: 'warning' | 'success' }[] = [
+  { key: 'boxPack', label: '绠卞寘瑁呰川閲�', tone: 'warning' },
+  { key: 'tiaoPack', label: '鏉″寘瑁呰川閲�', tone: 'warning' },
+  { key: 'hePack', label: '鐩掑寘瑁呰川閲�', tone: 'warning' },
+  { key: 'appearance', label: '澶栬', tone: 'warning' },
+  { key: 'hotStamp', label: '鐑伀', tone: 'warning' },
+  { key: 'endFade', label: '绔儴钀借壊', tone: 'success' },
+  { key: 'impurityRate', label: '鍚潅鐜�', tone: 'success' },
+  { key: 'moistureRate', label: '鍚按鐜�', tone: 'success' },
+  { key: 'length', label: '闀垮害', tone: 'success' },
+  { key: 'composite', label: '缁煎悎椤�', tone: 'success' }
+];
+
+const activeSegmentLabel = computed(() => segmentOptions.find(o => o.key === activeSegment.value)?.label ?? '');
+
+const summaryRows = ref([
+  {
+    sampleNo: 'DAC250312001',
+    brand: '閲戝湥(杞�)',
+    factory: '涓儫鍗风儫鍘�',
+    spec: '84.0 (脴24.0卤0.6), 0.0mm脳24.0mm',
+    tiaoCode: '6901028133241',
+    heCode: '6901028133234',
+    inspectMode: '瀹為獙瀹ゆ鏌�',
+    boxMark: 0,
+    tiaoheMark: 0,
+    heBarcode: 0,
+    tiaoheBarcode: 0,
+    syncResult: 0,
+    gradeA: 0,
+    gradeB: 0,
+    gradeC: 0,
+    appearance: 0,
+    score: 100,
+    conclusion: '/',
+    remark: '/'
+  }
+]);
+
+const detailRows = ref([
+  {
+    index: 1,
+    sampleNo: 'DAC250312001',
+    brand: '閲戝湥(杞�)',
+    factory: '涓儫鍗风儫鍘�',
+    productDate: '2025-03-11',
+    minor: 0,
+    medium: 0,
+    major: 0,
+    total: 0,
+    appearanceScore: 0,
+    appearanceDesc: '鍥炬枃娓呮櫚搴︼紝鏁存磥搴︾鍚堣姹傦紱琛ㄩ潰鏃犵牬鎹�',
+    compositeScore: 0,
+    compositeConclusion: '/',
+    remark: '/'
+  }
+]);
+
+const detailScrollRef = ref<HTMLElement | null>(null);
+const activeCell = ref<{ table: 'summary' | 'detail'; rowIndex: number; field: string } | null>(null);
+const editModalOpen = ref(false);
+const editValue = ref('');
+const editMeta = ref<{ table: 'summary' | 'detail'; rowIndex: number; field: string; label: string } | null>(
+  null
+);
+
+const summaryFieldLabels: Record<string, string> = {
+  sampleNo: '鏍峰搧缂栧彿',
+  brand: '鐗屽彿',
+  factory: '鐢熶骇鍘�',
+  spec: '瑙勬牸',
+  tiaoCode: '鏉$爜',
+  heCode: '鐩掔爜',
+  inspectMode: '妫�娴嬪舰寮�',
+  boxMark: '绠辨爣璇�',
+  tiaoheMark: '鏉$洅鏍囪瘑',
+  heBarcode: '鐩掓潯鐮�',
+  tiaoheBarcode: '鏉$洅鐮�',
+  syncResult: '鍚屾鐜板満缁撴灉',
+  gradeA: '绛夌骇A',
+  gradeB: '绛夌骇B',
+  gradeC: '绛夌骇C',
+  appearance: '澶栬鎵e垎',
+  score: '寰楀垎',
+  conclusion: '缁撹',
+  remark: '澶囨敞'
+};
+
+const detailFieldLabels: Record<string, string> = {
+  index: '搴忓彿',
+  sampleNo: '鏍峰搧缂栧彿',
+  brand: '鐗屽彿',
+  factory: '鐢熶骇鍘�',
+  productDate: '鐢熶骇鏃ユ湡',
+  minor: '灏忛」鎵e垎',
+  medium: '涓」鎵e垎',
+  major: '澶ч」鎵e垎',
+  total: '鍚堣',
+  appearanceScore: '鎵e垎',
+  appearanceDesc: '鎻忚堪',
+  compositeScore: '鎵e垎',
+  compositeConclusion: '缁撹',
+  remark: '澶囨敞'
+};
+
+function setZoom(next: number) {
+  const v = Math.max(0.6, Math.min(1.2, Number(next)));
+  zoom.value = Number.isFinite(v) ? v : 1;
+}
+
+function handlePrint() {
+  window.print();
+}
+
+function handleReset() {
+  activeSegment.value = 'syncResult';
+  activeBottom.value = 'composite';
+  setZoom(1);
+}
+
+function handleDetailScroll(direction: 'left' | 'right') {
+  const el = detailScrollRef.value;
+  if (!el) return;
+  const step = Math.max(200, Math.round(el.clientWidth * 0.6));
+  el.scrollBy({ left: direction === 'left' ? -step : step, behavior: 'smooth' });
+}
+
+function setActiveCell(table: 'summary' | 'detail', rowIndex: number, field: string) {
+  activeCell.value = { table, rowIndex, field };
+}
+
+function isActiveCell(table: 'summary' | 'detail', rowIndex: number, field: string) {
+  return (
+    activeCell.value?.table === table &&
+    activeCell.value?.rowIndex === rowIndex &&
+    activeCell.value?.field === field
+  );
+}
+
+function openEditModal(table: 'summary' | 'detail', rowIndex: number, field: string) {
+  setActiveCell(table, rowIndex, field);
+  const rows = table === 'summary' ? summaryRows.value : detailRows.value;
+  const row = rows[rowIndex];
+  if (!row) return;
+  const labelMap = table === 'summary' ? summaryFieldLabels : detailFieldLabels;
+  editMeta.value = {
+    table,
+    rowIndex,
+    field,
+    label: labelMap[field] ?? field
+  };
+  editValue.value = row[field as keyof typeof row] as string;
+  editModalOpen.value = true;
+}
+
+function handleEditConfirm() {
+  if (!editMeta.value) return;
+  const { table, rowIndex, field } = editMeta.value;
+  const rows = table === 'summary' ? summaryRows.value : detailRows.value;
+  const row = rows[rowIndex];
+  if (!row) return;
+  row[field as keyof typeof row] = editValue.value as never;
+  editModalOpen.value = false;
+}
+</script>
+
+<template>
+  <div class="h-full min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
+    <NCard
+      :bordered="false"
+      size="small"
+      class="card-wrapper flex flex-col sm:flex-1-hidden"
+      content-style="padding: 0; flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column;"
+    >
+      <div class="report-root">
+        <div class="report-toolbar">
+          <div class="flex-y-center gap-8px">
+            <NButton size="small" @click="handleReset">鍒锋柊</NButton>
+            <NButton size="small" type="primary" @click="handlePrint">鎵撳嵃</NButton>
+          </div>
+          <div class="flex-y-center gap-8px">
+            <span class="report-toolbar__label">缂╂斁</span>
+            <NButton size="small" @click="setZoom(zoom - 0.1)">-</NButton>
+            <NInputNumber v-model:value="zoom" size="small" :min="0.6" :max="1.2" :step="0.1" class="w-90px" />
+            <NButton size="small" @click="setZoom(zoom + 0.1)">+</NButton>
+          </div>
+        </div>
+
+        <div class="report-stage">
+          <div class="report-sheet" :style="{ transform: `scale(${zoom})` }">
+            <div class="report-title">鍗风儫浜у搧鍖呰鏍囪瘑璐ㄩ噺妫�楠岀粨鏋滄眹鎬昏〃</div>
+
+            <div class="report-table-scroll">
+              <table class="report-table">
+                <thead>
+                <tr>
+                  <th rowspan="2">鏍峰搧缂栧彿</th>
+                  <th rowspan="2">鐗屽彿</th>
+                  <th rowspan="2">鐢熶骇鍘�</th>
+                  <th rowspan="2">瑙勬牸</th>
+                  <th rowspan="2">鏉$爜</th>
+                  <th rowspan="2">鐩掔爜</th>
+                  <th rowspan="2">妫�娴嬪舰寮�</th>
+                  <th colspan="5">鍖呰鏍囪瘑</th>
+                  <th colspan="3">鏉$爜璇嗗埆绛夌骇</th>
+                  <th rowspan="2">澶栬鎵e垎</th>
+                  <th rowspan="2">寰楀垎</th>
+                  <th rowspan="2">缁撹</th>
+                  <th rowspan="2">澶囨敞锛堢己闄锋弿杩�/闂鍐呭锛�</th>
+                </tr>
+                <tr>
+                  <th>绠辨爣璇�</th>
+                  <th>鏉$洅鏍囪瘑</th>
+                  <th>鐩掓潯鐮�</th>
+                  <th>鏉$洅鐮�</th>
+                  <th>鍚屾鐜板満缁撴灉</th>
+                  <th>绛夌骇A</th>
+                  <th>绛夌骇B</th>
+                  <th>绛夌骇C</th>
+                </tr>
+                </thead>
+                <tbody>
+                <tr v-for="(row, rowIndex) in summaryRows" :key="row.sampleNo">
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'sampleNo') }"
+                    @click="setActiveCell('summary', rowIndex, 'sampleNo')"
+                    @dblclick="openEditModal('summary', rowIndex, 'sampleNo')"
+                  >
+                    {{ row.sampleNo }}
+                  </td>
+                  <td
+                    class="report-cell report-link"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'brand') }"
+                    @click="setActiveCell('summary', rowIndex, 'brand')"
+                    @dblclick="openEditModal('summary', rowIndex, 'brand')"
+                  >
+                    {{ row.brand }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'factory') }"
+                    @click="setActiveCell('summary', rowIndex, 'factory')"
+                    @dblclick="openEditModal('summary', rowIndex, 'factory')"
+                  >
+                    {{ row.factory }}
+                  </td>
+                  <td
+                    class="report-cell report-left"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'spec') }"
+                    @click="setActiveCell('summary', rowIndex, 'spec')"
+                    @dblclick="openEditModal('summary', rowIndex, 'spec')"
+                  >
+                    {{ row.spec }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'tiaoCode') }"
+                    @click="setActiveCell('summary', rowIndex, 'tiaoCode')"
+                    @dblclick="openEditModal('summary', rowIndex, 'tiaoCode')"
+                  >
+                    {{ row.tiaoCode }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'heCode') }"
+                    @click="setActiveCell('summary', rowIndex, 'heCode')"
+                    @dblclick="openEditModal('summary', rowIndex, 'heCode')"
+                  >
+                    {{ row.heCode }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'inspectMode') }"
+                    @click="setActiveCell('summary', rowIndex, 'inspectMode')"
+                    @dblclick="openEditModal('summary', rowIndex, 'inspectMode')"
+                  >
+                    {{ row.inspectMode }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'boxMark') }"
+                    @click="setActiveCell('summary', rowIndex, 'boxMark')"
+                    @dblclick="openEditModal('summary', rowIndex, 'boxMark')"
+                  >
+                    {{ row.boxMark }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'tiaoheMark') }"
+                    @click="setActiveCell('summary', rowIndex, 'tiaoheMark')"
+                    @dblclick="openEditModal('summary', rowIndex, 'tiaoheMark')"
+                  >
+                    {{ row.tiaoheMark }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'heBarcode') }"
+                    @click="setActiveCell('summary', rowIndex, 'heBarcode')"
+                    @dblclick="openEditModal('summary', rowIndex, 'heBarcode')"
+                  >
+                    {{ row.heBarcode }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'tiaoheBarcode') }"
+                    @click="setActiveCell('summary', rowIndex, 'tiaoheBarcode')"
+                    @dblclick="openEditModal('summary', rowIndex, 'tiaoheBarcode')"
+                  >
+                    {{ row.tiaoheBarcode }}
+                  </td>
+                  <td
+                    class="report-cell report-cell--active"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'syncResult') }"
+                    @click="setActiveCell('summary', rowIndex, 'syncResult')"
+                    @dblclick="openEditModal('summary', rowIndex, 'syncResult')"
+                  >
+                    {{ row.syncResult }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'gradeA') }"
+                    @click="setActiveCell('summary', rowIndex, 'gradeA')"
+                    @dblclick="openEditModal('summary', rowIndex, 'gradeA')"
+                  >
+                    {{ row.gradeA }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'gradeB') }"
+                    @click="setActiveCell('summary', rowIndex, 'gradeB')"
+                    @dblclick="openEditModal('summary', rowIndex, 'gradeB')"
+                  >
+                    {{ row.gradeB }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'gradeC') }"
+                    @click="setActiveCell('summary', rowIndex, 'gradeC')"
+                    @dblclick="openEditModal('summary', rowIndex, 'gradeC')"
+                  >
+                    {{ row.gradeC }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'appearance') }"
+                    @click="setActiveCell('summary', rowIndex, 'appearance')"
+                    @dblclick="openEditModal('summary', rowIndex, 'appearance')"
+                  >
+                    {{ row.appearance }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'score') }"
+                    @click="setActiveCell('summary', rowIndex, 'score')"
+                    @dblclick="openEditModal('summary', rowIndex, 'score')"
+                  >
+                    {{ row.score }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'conclusion') }"
+                    @click="setActiveCell('summary', rowIndex, 'conclusion')"
+                    @dblclick="openEditModal('summary', rowIndex, 'conclusion')"
+                  >
+                    {{ row.conclusion }}
+                  </td>
+                  <td
+                    class="report-cell report-left"
+                    :class="{ 'report-cell--selected': isActiveCell('summary', rowIndex, 'remark') }"
+                    @click="setActiveCell('summary', rowIndex, 'remark')"
+                    @dblclick="openEditModal('summary', rowIndex, 'remark')"
+                  >
+                    {{ row.remark }}
+                  </td>
+                </tr>
+                </tbody>
+              </table>
+            </div>
+
+            <div class="report-signature">
+              <div>鎵规浜猴細</div>
+              <div>瀹℃牳浜猴細</div>
+              <div>鍒惰〃浜猴細</div>
+            </div>
+
+            <div class="report-segment">
+              <div class="report-segment__inner">
+                <button
+                  v-for="opt in segmentOptions"
+                  :key="opt.key"
+                  class="report-pill"
+                  :class="[
+                    `report-pill--${opt.tone}`,
+                    { 'report-pill--active': opt.key === activeSegment }
+                  ]"
+                  type="button"
+                  @click="activeSegment = opt.key"
+                >
+                  {{ opt.label }}
+                </button>
+              </div>
+            </div>
+
+            <div class="report-title report-title--sub">鍗风儫鍖呰妫�楠岀粨鏋滄眹鎬昏〃</div>
+
+            <div class="report-table-scroll" ref="detailScrollRef">
+              <table class="report-table report-table--detail">
+                <thead>
+                <tr>
+                  <th rowspan="2">搴忓彿</th>
+                  <th rowspan="2">鏍峰搧缂栧彿</th>
+                  <th rowspan="2">鐗屽彿</th>
+                  <th rowspan="2">鐢熶骇鍘�</th>
+                  <th rowspan="2">鐢熶骇鏃ユ湡</th>
+                  <th colspan="4">{{ activeSegmentLabel }}</th>
+                  <th colspan="2">澶栬</th>
+                  <th colspan="2">缁煎悎椤�</th>
+                  <th rowspan="2">澶囨敞</th>
+                </tr>
+                <tr>
+                  <th>灏忛」鎵e垎</th>
+                  <th>涓」鎵e垎</th>
+                  <th>澶ч」鎵e垎</th>
+                  <th>鍚堣</th>
+                  <th>鎵e垎</th>
+                  <th>鎻忚堪</th>
+                  <th>鎵e垎</th>
+                  <th>缁撹</th>
+                </tr>
+                </thead>
+                <tbody>
+                <tr v-for="(row, rowIndex) in detailRows" :key="row.index">
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'index') }"
+                    @click="setActiveCell('detail', rowIndex, 'index')"
+                    @dblclick="openEditModal('detail', rowIndex, 'index')"
+                  >
+                    {{ row.index }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'sampleNo') }"
+                    @click="setActiveCell('detail', rowIndex, 'sampleNo')"
+                    @dblclick="openEditModal('detail', rowIndex, 'sampleNo')"
+                  >
+                    {{ row.sampleNo }}
+                  </td>
+                  <td
+                    class="report-cell report-link"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'brand') }"
+                    @click="setActiveCell('detail', rowIndex, 'brand')"
+                    @dblclick="openEditModal('detail', rowIndex, 'brand')"
+                  >
+                    {{ row.brand }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'factory') }"
+                    @click="setActiveCell('detail', rowIndex, 'factory')"
+                    @dblclick="openEditModal('detail', rowIndex, 'factory')"
+                  >
+                    {{ row.factory }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'productDate') }"
+                    @click="setActiveCell('detail', rowIndex, 'productDate')"
+                    @dblclick="openEditModal('detail', rowIndex, 'productDate')"
+                  >
+                    {{ row.productDate }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'minor') }"
+                    @click="setActiveCell('detail', rowIndex, 'minor')"
+                    @dblclick="openEditModal('detail', rowIndex, 'minor')"
+                  >
+                    {{ row.minor }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'medium') }"
+                    @click="setActiveCell('detail', rowIndex, 'medium')"
+                    @dblclick="openEditModal('detail', rowIndex, 'medium')"
+                  >
+                    {{ row.medium }}
+                  </td>
+                  <td
+                    class="report-cell report-cell--danger"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'major') }"
+                    @click="setActiveCell('detail', rowIndex, 'major')"
+                    @dblclick="openEditModal('detail', rowIndex, 'major')"
+                  >
+                    {{ row.major }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'total') }"
+                    @click="setActiveCell('detail', rowIndex, 'total')"
+                    @dblclick="openEditModal('detail', rowIndex, 'total')"
+                  >
+                    {{ row.total }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'appearanceScore') }"
+                    @click="setActiveCell('detail', rowIndex, 'appearanceScore')"
+                    @dblclick="openEditModal('detail', rowIndex, 'appearanceScore')"
+                  >
+                    {{ row.appearanceScore }}
+                  </td>
+                  <td
+                    class="report-cell report-left report-cell--hint"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'appearanceDesc') }"
+                    @click="setActiveCell('detail', rowIndex, 'appearanceDesc')"
+                    @dblclick="openEditModal('detail', rowIndex, 'appearanceDesc')"
+                  >
+                    {{ row.appearanceDesc }}
+                  </td>
+                  <td
+                    class="report-cell"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'compositeScore') }"
+                    @click="setActiveCell('detail', rowIndex, 'compositeScore')"
+                    @dblclick="openEditModal('detail', rowIndex, 'compositeScore')"
+                  >
+                    {{ row.compositeScore }}
+                  </td>
+                  <td
+                    class="report-cell report-cell--hint"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'compositeConclusion') }"
+                    @click="setActiveCell('detail', rowIndex, 'compositeConclusion')"
+                    @dblclick="openEditModal('detail', rowIndex, 'compositeConclusion')"
+                  >
+                    {{ row.compositeConclusion }}
+                  </td>
+                  <td
+                    class="report-cell report-left report-cell--hint"
+                    :class="{ 'report-cell--selected': isActiveCell('detail', rowIndex, 'remark') }"
+                    @click="setActiveCell('detail', rowIndex, 'remark')"
+                    @dblclick="openEditModal('detail', rowIndex, 'remark')"
+                  >
+                    {{ row.remark }}
+                  </td>
+                </tr>
+                </tbody>
+              </table>
+            </div>
+
+            <div class="report-pager">
+              <button class="report-pager__btn" type="button" @click="handleDetailScroll('left')">&lsaquo;</button>
+              <div class="report-pager__bar"></div>
+              <button class="report-pager__btn" type="button" @click="handleDetailScroll('right')">&rsaquo;</button>
+            </div>
+
+            <div class="report-bottom">
+              <div class="report-bottom__chips">
+                <button
+                  v-for="opt in bottomOptions"
+                  :key="opt.key"
+                  class="report-pill report-pill--bottom"
+                  :class="[
+                    `report-pill--${opt.tone}`,
+                    { 'report-pill--active': opt.key === activeBottom }
+                  ]"
+                  type="button"
+                  @click="activeBottom = opt.key"
+                >
+                  {{ opt.label }}
+                </button>
+              </div>
+
+              <div class="report-bottom__actions">
+                <NButton size="small" secondary @click="handleReset">鍒锋柊</NButton>
+                <NButton size="small" secondary @click="handlePrint">鎵撳嵃</NButton>
+                <NButton size="small" type="primary">鎺ㄩ��</NButton>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </NCard>
+    <NModal v-model:show="editModalOpen" :mask-closable="false">
+      <NCard
+        :title="editMeta ? `淇敼${editMeta.label}` : '淇敼鍐呭'"
+        style="width: 420px"
+        size="small"
+        closable
+        @close="editModalOpen = false"
+      >
+        <div class="flex flex-col gap-12px">
+          <NInput v-model:value="editValue" type="textarea" :autosize="{ minRows: 2, maxRows: 6 }" />
+          <div class="flex justify-end gap-8px">
+            <NButton size="small" @click="editModalOpen = false">鍙栨秷</NButton>
+            <NButton size="small" type="primary" @click="handleEditConfirm">纭畾</NButton>
+          </div>
+        </div>
+      </NCard>
+    </NModal>
+  </div>
+</template>
+
+<style scoped>
+.report-root {
+  background: #f3f4f6;
+  padding: 12px;
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+  min-height: 0;
+}
+
+.report-toolbar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 12px;
+  background: #fff;
+  border-bottom: 1px solid #e5e7eb;
+  flex: none;
+}
+
+.report-toolbar__label {
+  font-size: 12px;
+  color: #6b7280;
+}
+
+.report-stage {
+  padding: 12px;
+  overflow: auto;
+  flex: 1;
+  min-height: 0;
+}
+
+.report-sheet {
+  transform-origin: top center;
+  background: #fff;
+  width: 1200px;
+  margin: 0 auto;
+  padding: 18px 18px 16px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
+}
+
+.report-title {
+  text-align: center;
+  font-size: 18px;
+  font-weight: 600;
+  padding: 10px 0 12px;
+  color: #111827;
+}
+
+.report-title--sub {
+  font-size: 16px;
+  padding-top: 14px;
+}
+
+.report-table-scroll {
+  width: 100%;
+  overflow-x: auto;
+  overflow-y: hidden;
+}
+
+.report-table {
+  width: 100%;
+  min-width: 1160px;
+  border-collapse: collapse;
+  table-layout: fixed;
+  font-size: 12px;
+  color: #111827;
+}
+
+.report-table--detail {
+  min-width: 1400px;
+}
+
+.report-table th,
+.report-table td {
+  border: 1px solid #3f3f46;
+  padding: 6px 6px;
+  text-align: center;
+  vertical-align: middle;
+  line-height: 1.2;
+}
+
+.report-table th {
+  background: #fafafa;
+  font-weight: 600;
+}
+
+.report-left {
+  text-align: left;
+}
+
+.report-link {
+  color: #2563eb;
+  cursor: pointer;
+}
+
+.report-cell--selected {
+  outline: 2px solid rgb(var(--primary-color));
+  outline-offset: -2px;
+}
+
+.report-signature {
+  display: grid;
+  grid-template-columns: 1fr 1fr 1fr;
+  gap: 8px;
+  padding: 8px 0 2px;
+  color: #111827;
+  font-size: 12px;
+}
+
+.report-segment {
+  margin: 10px 0 12px;
+  background: #efefef;
+  padding: 10px 10px;
+}
+
+.report-segment__inner {
+  display: flex;
+  justify-content: center;
+  gap: 10px;
+  flex-wrap: wrap;
+}
+
+.report-pill {
+  border: 1px solid rgba(0, 0, 0, 0.08);
+  padding: 6px 14px;
+  font-size: 12px;
+  border-radius: 3px;
+  color: #111827;
+  background: #fff;
+  cursor: pointer;
+  user-select: none;
+}
+
+.report-pill--bottom {
+  padding: 6px 12px;
+}
+
+.report-pill--warning {
+  background: #f59e0b;
+  color: #fff;
+}
+
+.report-pill--success {
+  background: #10b981;
+  color: #fff;
+}
+
+.report-pill--info {
+  background: #0ea5e9;
+  color: #fff;
+}
+
+.report-pill--active {
+  outline: 2px solid rgba(17, 24, 39, 0.25);
+  outline-offset: 1px;
+}
+
+.report-cell--active {
+  background: #a5b4fc;
+}
+
+.report-cell--hint {
+  background: #c4b5fd;
+}
+
+.report-cell--danger {
+  color: #ef4444;
+  background: #fef2f2;
+  font-weight: 700;
+}
+
+.report-pager {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 10px 0 14px;
+}
+
+.report-pager__btn {
+  width: 28px;
+  height: 28px;
+  border: 1px solid #e5e7eb;
+  border-radius: 999px;
+  background: #fff;
+  cursor: pointer;
+  color: #111827;
+  font-size: 18px;
+  line-height: 26px;
+  text-align: center;
+}
+
+.report-pager__bar {
+  flex: 1;
+  height: 10px;
+  background: #d4d4d8;
+  border-radius: 999px;
+}
+
+.report-bottom {
+  background: #efefef;
+  padding: 12px 10px;
+}
+
+.report-bottom__chips {
+  display: flex;
+  justify-content: center;
+  flex-wrap: wrap;
+  gap: 10px;
+  margin-bottom: 12px;
+}
+
+.report-bottom__actions {
+  display: flex;
+  justify-content: center;
+  gap: 12px;
+}
+
+@media print {
+  .report-root {
+    background: #fff;
+    padding: 0;
+  }
+
+  .report-toolbar,
+  .report-pager,
+  .report-bottom {
+    display: none;
+  }
+
+  .report-stage {
+    padding: 0;
+    overflow: visible;
+  }
+
+  .report-sheet {
+    width: auto;
+    margin: 0;
+    padding: 0;
+    box-shadow: none;
+    transform: none !important;
+  }
+}
+</style>

--
Gitblit v1.9.3