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')">‹</button>
+ <div class="report-pager__bar"></div>
+ <button class="report-pager__btn" type="button" @click="handleDetailScroll('right')">›</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