From 3e069207503d4685a20ab5c695b1a17993f115ea Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期三, 09 九月 2026 15:56:45 +0800
Subject: [PATCH] fix: 09-09下午第三轮——审核结果默认按企业合并与问题范围语义收敛/导出企业合并问题列按用户4条例文话术、数据查看工具栏整理、报表整本预览分组表格化 + 测试报告与工作日结

---
 traffic-audit-web/src/views/AuditResult.vue |  382 +++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 290 insertions(+), 92 deletions(-)

diff --git a/traffic-audit-web/src/views/AuditResult.vue b/traffic-audit-web/src/views/AuditResult.vue
index fb821eb..9a78adf 100644
--- a/traffic-audit-web/src/views/AuditResult.vue
+++ b/traffic-audit-web/src/views/AuditResult.vue
@@ -17,6 +17,9 @@
       <el-main :class="{ 'main-focus': focusMode }" style="display:flex;flex-direction:column;min-height:0">
         <h3 v-show="!focusMode" style="flex:none;margin:0 0 10px">瀹℃牳缁撴灉</h3>
         <el-form :inline="true" style="flex:none">
+          <el-form-item label="鎶ヨ〃鏈�">
+            <el-date-picker v-model="period" type="month" placeholder="閫夋嫨鏈堜唤" value-format="yyyy-MM" @change="onPeriodChange"></el-date-picker>
+          </el-form-item>
           <el-form-item label="鎶ヨ〃绫诲瀷">
             <el-radio-group v-model="reportType" @change="onTypeChange">
               <el-radio label="H2032">璐ц繍鏈堟姤</el-radio>
@@ -30,9 +33,7 @@
             
             </el-radio-group>
           </el-form-item>
-          <el-form-item label="鎶ヨ〃鏈�">
-            <el-date-picker v-model="period" type="month" placeholder="閫夋嫨鏈堜唤" value-format="yyyy-MM" @change="onPeriodChange"></el-date-picker>
-          </el-form-item>
+
           <el-form-item>
             <el-button type="primary" @click="executeAudit" :loading="loading">鎵ц瀹℃牳</el-button>
             <el-button type="success" plain @click="markAudited" :loading="marking">鏍囪宸插鏍�</el-button>
@@ -49,13 +50,29 @@
           <div class="sev-card sev-pending"><b>{{ severityStats.pending }}</b> 寰呭鐞�</div>
           <div class="sev-card sev-done"><b>{{ severityStats.done }}</b> 宸插鐞�</div>
         </div>
-        <el-form v-if="!focusMode" :inline="true" style="margin-top:8px;flex:none">
+        <div v-if="!focusMode" class="audit-filter-bar">
+        <el-form :inline="true">
+          <el-form-item label="绛涢��"></el-form-item>
           <el-form-item label="浼佷笟鍚嶇О">
             <el-input v-model="enterpriseFilter" placeholder="杈撳叆浼佷笟鍚嶇О鍏抽敭瀛�" clearable style="width:200px" @keyup.enter.native="queryResults"></el-input>
           </el-form-item>
           <el-form-item label="瀹℃牳瑙勫垯">
             <el-select v-model="ruleFilter" placeholder="鍏ㄩ儴瑙勫垯" clearable style="width:180px">
               <el-option v-for="rn in ruleOptions" :key="rn" :label="rn" :value="rn"></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="闂鑼冨洿">
+            <el-radio-group v-model="issueScope" size="small" @change="updateBatchSelection">
+              <el-radio-button label="verify">浠呴渶鏍稿疄</el-radio-button>
+              <el-radio-button label="all">鍚彁閱掑瀷</el-radio-button>
+            </el-radio-group>
+            <span class="hint">鎻愰啋鍨嬩粎鎻愮ず鍙樺寲銆佷笉闇�澶勭悊锛屽鍑洪粯璁や笉鍚�</span>
+          </el-form-item>
+          <el-form-item label="鐘舵��">
+            <el-select v-model="statusScope" style="width:110px" @change="updateBatchSelection">
+              <el-option label="鍏ㄩ儴" value="all"></el-option>
+              <el-option label="寰呭鐞�" value="pending"></el-option>
+              <el-option label="宸插鐞�" value="handled"></el-option>
             </el-select>
           </el-form-item>
           <el-form-item label="瑙嗗浘">
@@ -73,9 +90,11 @@
             <el-button size="small" plain @click="statsCollapsed = !statsCollapsed">{{ statsCollapsed ? '灞曞紑' : '鏀惰捣' }}瑙勫垯缁熻</el-button>
           </el-form-item>
           <el-form-item>
-            <span style="color:#909399;font-size:12px">鍏� {{ results.length }} 鏉¢棶棰橈紝{{ mergedResults.length }} 瀹朵紒涓�</span>
+            <span style="color:#303133;font-size:13px;font-weight:600">褰撳墠绛涢�� {{ filteredResults.length }} 鏉� / {{ mergedResults.length }} 瀹朵紒涓�</span>
+            <span style="color:#909399;font-size:12px;margin-left:8px">锛堝叡 {{ results.length }} 鏉¢棶棰橈級</span>
           </el-form-item>
         </el-form>
+        </div>
         <div v-if="ruleStats.length && !statsCollapsed && !focusMode" class="rule-stats" style="flex:none">
           <div class="stats-cards">
             <div v-for="rs in sortedRuleStats" :key="rs.ruleId" class="stat-card"
@@ -97,65 +116,52 @@
           鍏� {{ results.length }} 鏉¢棶棰橈紝{{ mergedResults.length }} 瀹朵紒涓�
         </div>
         <div class="table-area" style="flex:1;min-height:0">
-        <!-- 鏄庣粏瑙嗗浘锛氫竴鏉¢棶棰樹竴寮犲崱鐗� -->
+        <!-- 鏄庣粏瑙嗗浘锛氫竴鏉¢棶棰樹竴琛岋紙涓庡鍑烘ā鏉� Sheet鈥滃鏍哥粨鏋溾�濆悓娆撅級 -->
         <div v-if="viewMode === 'detail'" class="detail-cards-wrap" v-loading="loading">
           <div class="detail-toolbar">
             <el-checkbox :value="allSelected" :indeterminate="someSelected" @change="toggleSelectAll">鍏ㄩ�夊綋鍓� {{ filteredResults.length }} 鏉�</el-checkbox>
             <span class="toolbar-tip">鍕鹃�夊悗鍙壒閲忕‘璁� / 蹇界暐</span>
           </div>
-          <div v-for="row in filteredResults" :key="row.id" class="detail-card"
-               :class="{ 'card-red': row.alertLevel === 'RED', 'card-yellow': row.alertLevel === 'YELLOW', 'card-done': row.status !== 'PENDING' }">
-            <div class="card-head">
-              <el-checkbox v-model="row._selected" @change="updateBatchSelection"></el-checkbox>
-              <div class="card-ent">
-                <div class="ent-name">{{ row.enterpriseName || row.city || '鏈煡浼佷笟' }}</div>
-                <div class="ent-sub">{{ row.reportPeriod }}</div>
-              </div>
-              <el-tag :type="row.alertLevel === 'RED' ? 'danger' : 'warning'" size="small" effect="dark">{{ row.alertLevel }}</el-tag>
-              <el-tag :type="statusType(row.status)" size="small" effect="plain" class="status-tag">
-                <i :class="statusIcon(row.status)"></i> {{ statusText(row.status) }}
-              </el-tag>
-              <span class="spacer"></span>
-              <el-link v-if="dataType(reportType)" type="primary" :underline="false" size="mini" @click="goData(row)">鏁版嵁</el-link>
-              <el-tag v-if="explainType(reportType)" size="mini" :type="row.verifyExplanation ? 'success' : 'warning'"
-                       style="cursor:pointer" :title="expandedExplain === row.reportId ? '鏀惰捣瑙i噴' : '灞曞紑濉啓/鏌ョ湅瑙i噴'"
-                       @click="toggleExplain(row.reportId)">
-                {{ row.verifyExplanation ? '瑙i噴锛氬凡濉啓' : '瑙i噴锛氭湭濉啓' }}
-              </el-tag>
-              <el-button v-if="row.status === 'PENDING'" size="mini" type="success" icon="el-icon-check" round @click="review(row, 'CONFIRMED')">纭</el-button>
-              <el-button v-if="row.status === 'PENDING'" size="mini" type="info" icon="el-icon-close" round plain @click="review(row, 'IGNORED')">蹇界暐</el-button>
-              <span v-else class="done-text">宸插鐞�</span>
+          <el-table :data="filteredResults" border stripe height="100%"
+            :header-cell-style="{ background: '#F5F7FA', color: '#303133', fontWeight: 600 }">
+            <el-table-column width="40">
+              <template slot-scope="{ row }">
+                <el-checkbox v-model="row._selected" @change="updateBatchSelection"></el-checkbox>
+              </template>
+            </el-table-column>
+            <el-table-column prop="reportPeriod" label="鎶ヨ〃鏈�" width="90"></el-table-column>
+            <el-table-column label="浼佷笟/甯傚窞" min-width="200" show-overflow-tooltip>
+              <template slot-scope="{ row }">{{ row.enterpriseName || row.city || '鏈煡浼佷笟' }}</template>
+            </el-table-column>
+            <el-table-column prop="ruleCode" label="瑙勫垯缂栫爜" width="100"></el-table-column>
+            <el-table-column prop="ruleName" label="瀹℃牳瑙勫垯" min-width="150" show-overflow-tooltip></el-table-column>
+            <el-table-column label="绛夌骇" width="76">
+              <template slot-scope="{ row }">
+                <el-tag :type="row.alertLevel === 'RED' ? 'danger' : 'warning'" size="mini">{{ row.alertLevel }}</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column prop="actualValue" label="瀹為檯鍊�" min-width="230" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="thresholdValue" label="闃堝��/鍩哄噯" min-width="140" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="deviation" label="鍋忓樊璇存槑" min-width="170" show-overflow-tooltip></el-table-column>
+            <el-table-column label="鐘舵��" width="92">
+              <template slot-scope="{ row }">
+                <el-tag :type="statusType(row.status)" size="mini">
+                  <i :class="statusIcon(row.status)"></i> {{ statusText(row.status) }}
+                </el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔" width="185" align="right">
+              <template slot-scope="{ row }">
+                <el-link v-if="dataType(reportType)" type="primary" :underline="false" size="mini" @click="openMonthReport(row)">鏁版嵁</el-link>
+                <el-button v-if="row.status === 'PENDING'" size="mini" type="success" icon="el-icon-check" round @click="review(row, 'CONFIRMED')">纭</el-button>
+                <el-button v-if="row.status === 'PENDING'" size="mini" type="info" icon="el-icon-close" round plain @click="review(row, 'IGNORED')">蹇界暐</el-button>
+                <span v-else class="done-text">宸插鐞�</span>
+              </template>
+            </el-table-column>
+            <div slot="empty">
+              <el-empty description="褰撳墠绛涢�夋潯浠朵笅娌℃湁闂鏁版嵁"></el-empty>
             </div>
-            <div class="card-body">
-              <div class="rule-title">
-                <b>{{ row.ruleName }}</b>
-                <span class="rule-code">{{ row.ruleCode }}</span>
-              </div>
-              <div class="card-details">
-                <div class="detail-line"><span class="dt-label">瀹為檯</span><span class="dt-val">{{ row.actualValue }}</span></div>
-                <div class="detail-line"><span class="dt-label">鍩哄噯</span><span class="dt-val">{{ row.thresholdValue }}</span></div>
-                <div class="detail-line"><span class="dt-label">宸紓</span><span class="dt-val dt-warn">{{ row.deviation }}</span></div>
-              </div>
-            </div>
-            <div v-if="explainType(reportType) && expandedExplain === row.reportId" class="explain-side" style="margin:0 12px 10px">
-              <div class="explain-head">
-                <span class="explain-title">浼佷笟瑙i噴</span>
-                <el-tag size="mini" :type="row.verifyExplanation ? 'success' : 'warning'">{{ row.verifyExplanation ? '宸插~鍐�' : '鏈~鍐�' }}</el-tag>
-                <span style="flex:1"></span>
-                <el-button size="mini" text @click="expandedExplain = null">鏀惰捣</el-button>
-              </div>
-              <span v-if="row.verifyExplanation" class="explain-cur">{{ row.verifyExplanation }}</span>
-              <el-input v-model="explainDrafts[row.reportId]" type="textarea" :rows="2" resize="none"
-                        placeholder="濉啓/淇敼璇ヤ紒涓氱殑瑙i噴璇存槑鈥�"></el-input>
-              <div class="explain-actions">
-                <el-button size="mini" type="primary" plain :loading="savingExplainId === row.reportId" @click="saveExplain(row)">淇濆瓨瑙i噴</el-button>
-                <el-button v-if="(explainDrafts[row.reportId] || '') !== (row.verifyExplanation || '')" size="mini" text @click="resetDraft(row.reportId)">閲嶇疆</el-button>
-              </div>
-            </div>
-          </div>
-          <div v-if="!filteredResults.length" class="empty-tip">
-            <el-empty description="褰撳墠绛涢�夋潯浠朵笅娌℃湁闂鏁版嵁"></el-empty>
-          </div>
+          </el-table>
         </div>
         <!-- 鍚堝苟瑙嗗浘 -->
         <el-table v-else :data="mergedResults" border stripe v-loading="loading" height="100%"
@@ -175,7 +181,7 @@
                   <b>{{ item.ruleName }}</b>
                   <el-tag :type="item.alertLevel === 'RED' ? 'danger' : 'warning'" size="mini">{{ item.alertLevel }}</el-tag>
                   <el-tag :type="statusType(item.status)" size="mini">{{ statusText(item.status) }}</el-tag>
-                  <el-link v-if="dataType(reportType)" type="primary" :underline="false" size="mini" @click="goData(item)">鏁版嵁</el-link>
+                  <el-link v-if="dataType(reportType)" type="primary" :underline="false" size="mini" @click="openMonthReport(item)">鏁版嵁</el-link>
                   <span style="flex:1"></span>
                   <el-button v-if="item.status === 'PENDING'" size="mini" type="success" @click="review(item, 'CONFIRMED')">纭</el-button>
                   <el-button v-if="item.status === 'PENDING'" size="mini" type="info" @click="review(item, 'IGNORED')">蹇界暐</el-button>
@@ -188,32 +194,6 @@
               </div>
             </template>
           </el-table-column>
-          <el-table-column label="浼佷笟瑙i噴" width="340">
-            <template slot-scope="scope">
-              <div v-if="explainType(reportType) && scope.row.items[0]" class="explain-cell">
-                <div v-if="expandedExplain !== scope.row.items[0].reportId" class="explain-compact">
-                  <el-tag size="mini" :type="scope.row.items[0].verifyExplanation ? 'success' : 'warning'">{{ scope.row.items[0].verifyExplanation ? '宸插~鍐�' : '鏈~鍐�' }}</el-tag>
-                  <div v-if="scope.row.items[0].verifyExplanation" class="explain-preview">{{ scope.row.items[0].verifyExplanation }}</div>
-                  <el-button size="mini" text type="primary" @click="expandedExplain = scope.row.items[0].reportId">濉啓/淇敼</el-button>
-                </div>
-                <div v-else class="explain-side">
-                  <div class="explain-head">
-                    <span class="explain-title">浼佷笟瑙i噴</span>
-                    <span style="flex:1"></span>
-                    <el-button size="mini" text @click="expandedExplain = null">鏀惰捣</el-button>
-                  </div>
-                  <span v-if="scope.row.items[0].verifyExplanation" class="explain-cur">{{ scope.row.items[0].verifyExplanation }}</span>
-                  <el-input v-model="explainDrafts[scope.row.items[0].reportId]" type="textarea" :rows="2" resize="none"
-                            placeholder="濉啓/淇敼璇ヤ紒涓氱殑瑙i噴璇存槑鈥�"></el-input>
-                  <div class="explain-actions">
-                    <el-button size="mini" type="primary" plain :loading="savingExplainId === scope.row.items[0].reportId" @click="saveExplain(scope.row.items[0])">淇濆瓨瑙i噴</el-button>
-                    <el-button v-if="(explainDrafts[scope.row.items[0].reportId] || '') !== (scope.row.items[0].verifyExplanation || '')" size="mini" text @click="resetDraft(scope.row.items[0].reportId)">閲嶇疆</el-button>
-                  </div>
-                </div>
-              </div>
-              <span v-else style="color:#C0C4CC;font-size:12px">鈥�</span>
-            </template>
-          </el-table-column>
           <el-table-column label="鎿嶄綔" width="130">
             <template slot-scope="scope">
               <el-button v-if="hasPending(scope.row)" size="mini" type="success" plain @click="reviewAll(scope.row, 'CONFIRMED')">鍏ㄩ儴纭</el-button>
@@ -224,6 +204,50 @@
         </div>
       </el-main>
     </el-container>
+
+        <el-dialog title="鏁版嵁鏌ョ湅锛堟湀鎶ユ牱寮忥級" :visible.sync="monthVisible" width="880px" top="4vh" custom-class="mr-dialog">
+          <div v-if="monthLoading" style="padding:40px 0;text-align:center;color:#909399">鍔犺浇涓��</div>
+          <div v-else-if="!monthRecords.length" style="padding:30px 0;text-align:center;color:#909399">
+            鏈煡璇㈠埌璇ヤ紒涓氥�佽鎶ヨ〃鏈熺殑濉姤鏁版嵁锛堝彲鑳借繕鏈鍏ワ級
+          </div>
+          <div v-else class="mr-body">
+            <div v-for="(rec, ri) in monthRecords" :key="ri" class="mr-card">
+              <div class="mr-title">{{ monthTitle(rec) }}</div>
+              <table class="mr-meta">
+                <tbody>
+                  <tr v-for="(m, mi) in monthMeta(rec)" :key="mi">
+                    <td class="mr-meta-k">{{ m.label }}</td>
+                    <td class="mr-meta-v">{{ fmtVal(m.value) }}</td>
+                  </tr>
+                </tbody>
+              </table>
+              <div v-for="sec in monthSections(rec)" :key="sec.title" class="mr-sec">
+                <div class="mr-sec-title">{{ sec.title }}</div>
+                <table class="mr-sec-tab" border="1">
+                  <thead>
+                    <tr>
+                      <th style="width:34%">鎸囨爣鍚嶇О</th>
+                      <th v-for="(h, hi) in sec.head" :key="hi" style="width:16.5%">{{ h }}</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    <tr v-for="(rw, rwi) in sec.rows" :key="rwi">
+                      <td>{{ rw.label }}</td>
+                      <td v-for="(c, ci) in rw.cells" :key="ci">{{ fmtVal(c.v) }}</td>
+                    </tr>
+                  </tbody>
+                </table>
+              </div>
+              <div v-if="rec.verifyExplanation || rec.reportNotes" class="mr-note">
+                <div v-if="rec.verifyExplanation" class="mr-note-line"><b>鏍稿疄瑙i噴锛�</b>{{ rec.verifyExplanation }}</div>
+                <div v-if="rec.reportNotes" class="mr-note-line"><b>澶囨敞锛�</b>{{ rec.reportNotes }}</div>
+              </div>
+            </div>
+          </div>
+          <span slot="footer">
+            <el-button size="small" @click="monthVisible = false">鍏抽棴</el-button>
+          </span>
+        </el-dialog>
   </el-container>
 </template>
 <script>
@@ -231,14 +255,113 @@
 import api from '@/api'
 const DATA_TYPE_MAP = { H2032: 'h2032', H2031: 'h2031', CITY_BUS: 'cityBus', CITY_TAXI: 'cityTaxi' }
 const EXPLAIN_TYPES = ['H2032', 'H2031', 'CITY_BUS', 'CITY_TAXI']
+
+const MONTH_CFG = {
+  h2032: {
+    meta: [['鎶ヨ〃鏈�', 'reportPeriod'], ['浼佷笟鍚嶇О', 'enterpriseName'], ['缁熶竴绀句細淇$敤浠g爜', 'unifiedCreditCode'], ['濉姤鍗曚綅', 'reportUnit']],
+    sections: [
+      { title: '涓�銆佽繍鍔涙儏鍐�', head: ['杞﹁締鏁帮紙杈嗭級', '鏍囪鍚ㄤ綅鏁帮紙鍚級'], rows: [
+        ['鎬昏', ['vehicleTotal', 'tonsTotal']],
+        ['鍏朵腑锛氱壍寮曡溅', ['vehicleTractor', null]],
+        ['鎸傝溅', ['vehicleTrailer', 'tonsTrailer']],
+        ['鍏朵腑锛氶泦瑁呯杞�', ['vehicleContainer', 'tonsContainer']],
+        ['鏁磋溅', ['vehicleWhole', 'tonsWhole']]
+      ]},
+      { title: '浜屻�佽繍杈撶敓浜ф儏鍐�', head: ['璐ц繍閲忥紙鍚級', '璐х墿鍛ㄨ浆閲忥紙鍚ㄥ叕閲岋級'], rows: [
+        ['杩愯緭閲�', ['freightTotal', 'turnoverTotal']],
+        ['鍏朵腑锛氶泦瑁呯', ['freightContainer', null]],
+        ['鍏朵腑锛氱叅鐐強鍒跺搧', ['freightCoal', null]],
+        ['鐭虫补銆佸ぉ鐒舵皵鍙婂埗鍝�', ['freightOilGas', null]],
+        ['鍏朵腑锛氬師娌�', ['freightCrudeOil', null]],
+        ['閲戝睘鐭跨煶', ['freightMetalOre', null]],
+        ['鍏朵腑锛氶搧鐭跨煶', ['freightIronOre', null]],
+        ['鐭垮缓鎬у缓绛戞潗鏂�', ['freightBuilding', null]],
+        ['绮', ['freightGrain', null]]
+      ]},
+      { title: '骞冲潎鎸囨爣', head: [], rows: [
+        ['璐ц溅骞冲潎鍚ㄤ綅', ['avgTonnage', null]],
+        ['璐ц溅骞冲潎杩愯窛', ['avgDistance', null]]
+      ]},
+      { title: '濉姤浜轰俊鎭�', head: [], rows: [
+        ['鍗曚綅璐熻矗浜�', ['unitLeader', null]], ['缁熻璐熻矗浜�', ['statsLeader', null]],
+        ['濉〃浜�', ['contactPerson', null]], ['鑱旂郴鐢佃瘽', ['contactPhone', null]], ['鎶ュ嚭鏃ユ湡', ['reportDate', null]]
+      ]}
+    ]
+  },
+  h2031: {
+    meta: [['鎶ヨ〃鏈�', 'reportPeriod'], ['浼佷笟鍚嶇О', 'enterpriseName']],
+    sections: [
+      { title: '涓�銆佽繍鍔涙儏鍐�', head: ['杞﹁締鏁帮紙杈嗭級', '鏍稿畾杞藉浣嶏紙瀹綅锛�'], rows: [
+        ['鎬昏', ['vehicleTotal', 'seatTotal']],
+        ['瀹㈣繍鐝溅锛堝惈瀹氱嚎鏃呮父瀹㈣繍瀹㈣溅锛�', ['vehicleSchedule', 'seatSchedule']],
+        ['瀹㈣繍鍖呰溅锛堝惈闈炲畾绾挎梾娓稿杩愬杞︼級', ['vehicleCharter', 'seatCharter']]
+      ]},
+      { title: '浜屻�佽繍杈撶敓浜ф儏鍐�', head: ['瀹㈣繍閲忥紙浜烘锛�', '鏃呭鍛ㄨ浆閲忥紙浜哄叕閲岋級'], rows: [
+        ['鎬昏', ['passengerTotal', 'turnoverTotal']],
+        ['涓�绫诲杩愮彮绾匡紙鍚畾绾挎梾娓革級', ['passengerClass1', 'turnoverClass1']],
+        ['浜岀被瀹㈣繍鐝嚎锛堝惈瀹氱嚎鏃呮父锛�', ['passengerClass2', 'turnoverClass2']],
+        ['涓夌被瀹㈣繍鐝嚎锛堝惈瀹氱嚎鏃呮父锛�', ['passengerClass3', 'turnoverClass3']],
+        ['鍥涚被瀹㈣繍鐝嚎锛堝惈瀹氱嚎鏃呮父锛�', ['passengerClass4', 'turnoverClass4']],
+        ['瀹㈣繍鍖呰溅锛堝惈闈炲畾绾挎梾娓革級', ['passengerCharter', 'turnoverCharter']]
+      ]},
+      { title: '瀹㈣繍骞冲潎杩愯窛', head: [], rows: [
+        ['瀹㈣繍骞冲潎杩愯窛', ['avgDistanceTotal', null]],
+        ['涓�绫诲杩愮彮绾垮钩鍧囪繍璺�', ['avgDistanceClass1', null]],
+        ['浜岀被瀹㈣繍鐝嚎骞冲潎杩愯窛', ['avgDistanceClass2', null]],
+        ['涓夌被瀹㈣繍鐝嚎骞冲潎杩愯窛', ['avgDistanceClass3', null]],
+        ['鍥涚被瀹㈣繍鐝嚎骞冲潎杩愯窛', ['avgDistanceClass4', null]],
+        ['瀹㈣繍鍖呰溅骞冲潎杩愯窛', ['avgDistanceCharter', null]]
+      ]}
+    ]
+  },
+  cityBus: {
+    meta: [['鎶ヨ〃鏈�', 'reportPeriod'], ['甯傚窞', 'city'], ['浼佷笟鍚嶇О', 'enterpriseName']],
+    sections: [
+      { title: '涓�銆佸叕鍏辨苯鐢佃溅杩愯惀', head: [], rows: [
+        ['杩愯惀杞︽暟锛堣締锛�', ['opVehicles', null]], ['杩愯惀绔欑偣鎬讳釜鏁帮紙涓級', ['stopTotal', null]],
+        ['鍏朵腑锛氬煄甯傝繍钀ョ珯鐐�', ['stopCity', null]], ['瀹㈣繍閲忥紙涓囦汉娆★級', ['passengerVolume', null]],
+        ['鍏朵腑锛氬煄甯傚杩愰噺', ['passengerCity', null]], ['鏃呭鍛ㄨ浆閲忥紙涓囦汉鍏噷锛�', ['turnover', null]],
+        ['鍏朵腑锛氬煄甯傛梾瀹㈠懆杞噺', ['turnoverCity', null]]
+      ]},
+      { title: '浜屻�佹淳鐢熸寚鏍囦笌澧為��', head: [], rows: [
+        ['骞冲潎杩愯窛锛堝叕閲岋級', ['avgDistance', null]], ['鍩庡競鍐呭钩鍧囪繍璺�', ['avgDistanceCity', null]],
+        ['鍩庨檯鍩庝埂瀹㈣繍閲�', ['passengerChengxiang', null]], ['鍩庨檯鍩庝埂鍛ㄨ浆閲�', ['turnoverChengxiang', null]],
+        ['鍩庨檯鍩庝埂骞冲潎杩愯窛', ['avgDistanceChengxiang', null]], ['瀹㈣繍閲忕幆姣�', ['ratePassengerMom', null]],
+        ['鍩庡競鍐呭杩愰噺鐜瘮', ['ratePassengerCityMom', null]], ['鍩庨檯鍩庝埂瀹㈣繍閲忕幆姣�', ['rateChengxiangMom', null]],
+        ['瀹㈣繍閲忓悓姣�', ['ratePassengerYoy', null]], ['鍩庡競鍐呭杩愰噺鍗犳瘮', ['passengerCityRatio', null]],
+        ['鍩庨檯鍩庝埂瀹㈣繍閲忓崰姣�', ['chengxiangRatio', null]], ['濉〃浜�', ['contactPerson', null]],
+        ['鎶ュ嚭鏃ユ湡', ['reportDate', null]]
+      ]}
+    ]
+  },
+  cityTaxi: {
+    meta: [['鎶ヨ〃鏈�', 'reportPeriod'], ['甯傚窞', 'city'], ['浼佷笟鍚嶇О', 'enterpriseName']],
+    sections: [
+      { title: '涓�銆佸贰娓稿嚭绉熻繍钀ユ湇鍔�', head: [], rows: [
+        ['杞藉杞︽鎬绘暟锛堜竾杞︽锛�', ['tripTotal', null]], ['鍏朵腑锛氬煄甯傝浇瀹㈣溅娆�', ['tripCity', null]],
+        ['瀹㈣繍閲忥紙涓囦汉娆★級', ['passengerVolume', null]], ['鍏朵腑锛氬煄甯傚杩愰噺', ['passengerCity', null]],
+        ['鏃呭鍛ㄨ浆閲忥紙涓囦汉鍏噷锛�', ['turnover', null]], ['鍏朵腑锛氬煄甯傛梾瀹㈠懆杞噺', ['turnoverCity', null]],
+        ['杩愯惀杞﹁締鏁帮紙杈嗭級', ['opVehicles', null]]
+      ]},
+      { title: '浜屻�佹淳鐢熸寚鏍囦笌澧為��', head: [], rows: [
+        ['骞冲潎杩愯窛锛堝叕閲岋級', ['avgDistance', null]], ['鍩庡競鍐呭钩鍧囪繍璺�', ['avgDistanceCity', null]],
+        ['鍗曡溅娆¤浇瀹汉鏁�', ['passengersPerTrip', null]], ['杞藉杞︽鐜瘮', ['rateTripMom', null]],
+        ['杞藉杞︽鍚屾瘮', ['rateTripYoy', null]], ['瀹㈣繍閲忕幆姣�', ['ratePassengerMom', null]],
+        ['瀹㈣繍閲忓悓姣�', ['ratePassengerYoy', null]], ['鍩庡競鍐呭杩愰噺鍗犳瘮', ['passengerCityRatio', null]],
+        ['鍩庨檯鍩庝埂骞冲潎杩愯窛', ['avgDistanceChengxiang', null]]
+      ]}
+    ]
+  }
+}
 export default {
   components: { AppHeader },
   name: 'AuditResult',
   data() {
     return {
       period: '', reportType: 'H2032', loading: false, marking: false, results: [], ruleStats: [], statsCollapsed: false,
-      enterpriseFilter: '', ruleFilter: '', viewMode: 'merged', batchSelection: [], focusMode: false,
-      explainDrafts: {}, savingExplainId: null, expandedExplain: null
+      enterpriseFilter: '', ruleFilter: '', viewMode: 'merged', issueScope: 'verify', statusScope: 'all', batchSelection: [], focusMode: false,
+      explainDrafts: {}, savingExplainId: null, expandedExplain: null,
+      monthVisible: false, monthLoading: false, monthType: '', monthPeriod: '', monthKeyword: '', monthRecords: [], monthColumns: []
     }
   },
   computed: {
@@ -249,7 +372,7 @@
     },
     severityStats() {
       const s = { red: 0, yellow: 0, pending: 0, done: 0 }
-      this.results.forEach(r => {
+      this.filteredResults.forEach(r => {
         if (r.alertLevel === 'RED') s.red++
         else if (r.alertLevel === 'YELLOW') s.yellow++
         if (r.status === 'PENDING') s.pending++
@@ -266,13 +389,20 @@
       const list = this.filteredResults
       return list.some(r => r._selected) && !this.allSelected
     },
+    isReminderRow(r) {
+      return !!(r && r.ruleCode === 'RULE_004')
+    },
     filteredResults() {
       const kw = (this.enterpriseFilter || '').trim()
       const rule = this.ruleFilter
-      if (!kw && !rule) return this.results
+      const scope = this.issueScope || 'verify'
+      const st = this.statusScope || 'all'
       return this.results.filter(r => {
         if (kw && !(r.enterpriseName || '').includes(kw)) return false
         if (rule && r.ruleName !== rule) return false
+        if (scope === 'verify' && this.isReminderRow(r)) return false
+        if (st === 'pending' && r.status !== 'PENDING') return false
+        if (st === 'handled' && (!r.status || r.status === 'PENDING')) return false
         return true
       })
     },
@@ -301,10 +431,56 @@
   methods: {
     dataType(t) { return DATA_TYPE_MAP[t] || '' },
     explainType(t) { return EXPLAIN_TYPES.indexOf(t) >= 0 },
-    goData(row) {
+    openMonthReport(row) {
       const type = DATA_TYPE_MAP[this.reportType]
       if (!type) return
-      this.$router.push({ path: '/data', query: { type, period: row.reportPeriod, keyword: row.enterpriseName || row.city || '' } })
+      this.monthType = type
+      this.monthPeriod = row.reportPeriod
+      this.monthKeyword = row.enterpriseName || row.city || ''
+      this.monthRecords = []
+      this.monthColumns = []
+      this.monthVisible = true
+      this.monthLoading = true
+      api.get('/data/list', {
+        params: {
+          type, period: row.reportPeriod,
+          keyword: row.enterpriseName || row.city || '',
+          page: 1, size: 50
+        }, timeout: 60000
+      }).then(res => {
+        const d = res.data || {}
+        this.monthRecords = d.records || []
+        this.monthColumns = d.columns || []
+        if (!this.monthRecords.length) this.$message.warning('鏈煡璇㈠埌璇ヤ紒涓氳鎶ヨ〃鏈熺殑濉姤鏁版嵁锛堝彲鑳藉皻鏈鍏ワ級')
+      }).catch(e => {
+        if (e && e._toast) return
+        this.$message.error('鏈堟姤鏁版嵁鏌ヨ澶辫触')
+      }).finally(() => { this.monthLoading = false })
+    },
+    monthTitle(rec) {
+      const cfg = MONTH_CFG[this.monthType] || null
+      if (!cfg) return (this.monthKeyword || '') + '锛�' + this.monthPeriod + '锛�'
+      const name = rec.enterpriseName || rec.city || this.monthKeyword || ''
+      return name + ' ' + (rec.reportPeriod || this.monthPeriod || '') + ' 鏈堟姤鏁版嵁'
+    },
+    monthMeta(rec) {
+      const cfg = MONTH_CFG[this.monthType] || null
+      if (!cfg) return []
+      return cfg.meta.map(m => ({ label: m[0], value: rec[m[1]] })).filter(m => m.value !== undefined)
+    },
+    monthSections(rec) {
+      const cfg = MONTH_CFG[this.monthType]
+      if (!cfg) return []
+      return cfg.sections.map(sec => ({
+        title: sec.title, head: sec.head || [],
+        rows: sec.rows.map(r => ({ label: r[0], cells: (r[1] || []).map(prop => ({ prop, v: prop ? rec[prop] : null })) }))
+          .filter(rw => rw.cells.some(c => c.v !== null && c.v !== undefined && c.v !== ''))
+      })).filter(sec => sec.rows.length)
+    },
+    fmtVal(v) {
+      if (v === null || v === undefined || v === '') return '鈥�'
+      if (typeof v === 'number') return String(v)
+      return String(v)
     },
     goExplain(row) {
       this.$router.push({ path: '/explain', query: { reportType: this.reportType, keyword: row.enterpriseName || row.city || '', period: row.reportPeriod } })
@@ -433,7 +609,8 @@
     },
     exportExcel() {
       if (!this.period) { this.$message.warning('璇烽�夋嫨鎶ヨ〃鏈�'); return }
-      window.open('/api/audit/export?period=' + this.period + '&reportType=' + this.reportType)
+      const q = 'period=' + this.period + '&reportType=' + this.reportType + '&onlyVerify=' + (this.issueScope !== 'all') + '&status=' + (this.statusScope || 'all')
+      window.open('/api/audit/export?' + q)
     },
     review(row, status) {
       if (status === 'IGNORED') {
@@ -581,4 +758,25 @@
 .stat-num { color: #909399; }
 .stat-num b { font-weight: 700; }
 .hit-num { color: #E6A23C; }
+
+.mr-dialog .el-dialog__body { padding: 12px 18px; }
+.mr-body { max-height: 66vh; overflow-y: auto; }
+.mr-card { border: 1px solid #DCDFE6; border-radius: 6px; margin-bottom: 14px; overflow: hidden; }
+.mr-title { background: #409EFF; color: #fff; font-weight: 600; padding: 8px 12px; font-size: 15px; }
+.mr-meta { width: 100%; border-collapse: collapse; background: #F7F9FC; }
+.mr-meta td { border: 1px solid #EBEEF5; padding: 6px 10px; font-size: 13px; }
+.mr-meta-k { width: 120px; color: #909399; background: #F0F4FA; }
+.mr-meta-v { color: #303133; font-weight: 600; }
+.mr-sec { padding: 10px 12px 4px; }
+.mr-sec-title { font-weight: 600; color: #303133; margin: 4px 0 8px; }
+.mr-sec-tab { width: 100%; border-collapse: collapse; font-size: 13px; }
+.mr-sec-tab th, .mr-sec-tab td { border: 1px solid #DCDFE6; padding: 5px 8px; text-align: left; color: #303133; }
+.mr-sec-tab th { background: #F5F7FA; font-weight: 600; }
+.mr-note { margin: 6px 12px 12px; background: #FDF6EC; border: 1px solid #FAECD8; border-radius: 4px; padding: 8px 10px; font-size: 12px; color: #8a6d3b; white-space: pre-wrap; word-break: break-all; }
+.mr-note-line { margin: 2px 0; }
+
+.audit-filter-bar { background:#FAFAFA; border:1px solid #EBEEF5; border-radius:8px; padding:2px 12px 0; margin-top:8px; flex:none; }
+.audit-filter-bar .el-form-item { margin-bottom:6px; }
+.audit-filter-bar .el-form-item__label { color:#606266; font-weight:600; }
+.hint { color:#909399; font-size:12px; margin-left:6px; white-space:nowrap; }
 </style>

--
Gitblit v1.9.1