From 7a17662bdadf36c4e30bd53b896571612d48abbb Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期三, 09 九月 2026 11:17:38 +0800
Subject: [PATCH] fix: 4项整改——审核结果按模板双Sheet导出(仅核实型+状态筛选)/明细表格化、数据查看分组选择与隐藏三列、报表页隐藏货运旅客城市客运小表、整本预览展示货运城市客运中口径当月累计同比 + 测试报告与工作日结

---
 traffic-audit-web/src/views/ReportExport.vue                                                      |   15 +
 traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java |  119 ++++++++++++
 docs/工作日结/工作日结_2026-09-09.md                                                                      |   16 +
 traffic-audit-web/src/views/DataView.vue                                                          |   82 +++++++--
 traffic-audit-server/src/main/java/com/trafficaudit/auditengine/controller/AuditController.java   |    6 
 traffic-audit-web/src/views/AuditResult.vue                                                       |  100 +++++++----
 docs/功能测试报告/功能测试报告_审核结果模板展示与导出及报表页整改_2026-09-09.md                                                |   41 ++++
 traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java   |  114 +++++++++++-
 8 files changed, 413 insertions(+), 80 deletions(-)

diff --git "a/docs/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212_\345\256\241\346\240\270\347\273\223\346\236\234\346\250\241\346\235\277\345\261\225\347\244\272\344\270\216\345\257\274\345\207\272\345\217\212\346\212\245\350\241\250\351\241\265\346\225\264\346\224\271_2026-09-09.md" "b/docs/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212_\345\256\241\346\240\270\347\273\223\346\236\234\346\250\241\346\235\277\345\261\225\347\244\272\344\270\216\345\257\274\345\207\272\345\217\212\346\212\245\350\241\250\351\241\265\346\225\264\346\224\271_2026-09-09.md"
new file mode 100644
index 0000000..0e227b9
--- /dev/null
+++ "b/docs/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212/\345\212\237\350\203\275\346\265\213\350\257\225\346\212\245\345\221\212_\345\256\241\346\240\270\347\273\223\346\236\234\346\250\241\346\235\277\345\261\225\347\244\272\344\270\216\345\257\274\345\207\272\345\217\212\346\212\245\350\241\250\351\241\265\346\225\264\346\224\271_2026-09-09.md"
@@ -0,0 +1,41 @@
+# 鍔熻兘娴嬭瘯鎶ュ憡锛氬鏍哥粨鏋滄ā鏉垮寲灞曠ず/瀵煎嚭涓庢暟鎹煡鐪嬨�佹姤琛ㄧ敓鎴愰〉鏁存敼
+
+鏃ユ湡锛�2026-09-09銆�妯″潡锛氬鏍哥粨鏋� / 鏁版嵁鏌ョ湅 / 鎶ヨ〃鐢熸垚銆�娴嬭瘯浜猴細Codex锛堜笟鍔′晶鐐归獙鍚庤ˉ鍏咃級
+
+## 涓�銆佸姛鑳借鏄�
+1. 瀹℃牳缁撴灉椤垫槑缁嗚鍥炬寜鐢ㄦ埛鏂版ā鏉匡紙docs/瀹℃牳鐣岄潰鏈堟姤妯℃澘/妯℃澘_瀹℃牳缁撴灉_.xlsx锛夊仛鎴� 10 鍒楀钩閾鸿〃鏍硷紱榛樿鍙湅銆屾牳瀹炲瀷銆嶉棶棰橈紙RULE_004 鐜瘮鎻愰啋榛樿鎺掗櫎锛夛紝鍙垏銆屽叏閮ㄣ�嶏紱鐘舵�佸彲鎸� 鍏ㄩ儴/寰呭鐞�/宸插鐞� 杩囨护锛屽鍑轰笌鐣岄潰绛涢�変竴鑷淬��
+2. 瀵煎嚭 Excel 鎸夋ā鏉垮弻 Sheet锛歚瀹℃牳缁撴灉`锛堥�愭潯锛夛紜`浼佷笟鍚堝苟`锛堟寜浼佷笟姹囨�� + 鑷姩缂栧彿闂鍒楋紝浼佷笟涓庨棶棰樺垪璺ㄨ鍚堝苟锛夈��
+3. 鏁版嵁鏌ョ湅锛氶《閮ㄦā鍧楋紜鏁版嵁绫诲瀷涓嬫媺涓庡鍏ラ〉涓�鑷达紙鏂囨宸插榻愬鍏ラ〉锛夛紝鎶ヨ〃鏈�/鍏抽敭瀛楁煡璇㈢Щ鍒扮浜岃锛涗紒涓氫唬鐮�/缁熶竴绀句細淇$敤浠g爜/濉姤鍗曚綅 3 鍒楅殣钘忋��
+4. 鎶ヨ〃鐢熸垚锛氳揣杩愭姤琛�/鏃呭鎶ヨ〃/鍩庡競瀹㈣繍鎶ヨ〃涓夌粍妯″潡闅愯棌锛屼粎淇濈暀 姹囨�诲ぇ琛�/鎶曡祫鎶ヨ〃/鑳借�楁姤琛紱閬撹矾杩愯緭閲忔眹鎬昏〃锛堟暣鏈級棰勮灞曠ず 璐ц繍閲�/璐ц繍鍛ㄨ浆閲忋�佸煄甯傚杩愰噺/鍛ㄨ浆閲忋�佷腑鍙e緞瀹㈣繍閲�/鍛ㄨ浆閲� 鐨� 褰撴湀/绱/鍚屾瘮銆�
+
+## 浜屻�佹祴璇曠幆澧�
+- 鍚庣 8090锛坢vn 缂栬瘧閫氳繃銆佸凡閲嶅惎灏辩华锛夛紝鍓嶇 8080锛坉ev 缂栬瘧鎴愬姛銆佺儹鏇存柊浜х墿鎶芥煡閫氳繃锛夈��
+- 鏁版嵁搴擄細鏈満 traffic_audit锛�2026-08 璐ц繍 H2032 宸插鏍告暟鎹湪搴擄級銆�
+
+## 涓夈�佺敤渚嬩笌缁撴灉
+| 缂栧彿 | 鐢ㄤ緥 | 杈撳叆 | 棰勬湡 | 瀹為檯 | 缁撹 |
+|---|---|---|---|---|---|
+| 1 | 瀹℃牳瀵煎嚭鍙� Sheet | /api/audit/export?period=2026-08&reportType=H2032&onlyVerify=true&status=all | 瀹℃牳缁撴灉10鍒�+浼佷笟鍚堝苟7鍒椼�侀棶棰樺垪鑷姩缂栧彿銆佷紒涓�/闂鍚堝苟 | 475 琛屆�10 鍒楋紱浼佷笟鍚堝苟 150 缁勫悎骞跺尯锛圔/G 鍒楋級锛涢棶棰樺舰濡傗��1銆佷笂鎶ュ懆杞噺涓庤建杩规祴绠椻�﹁鍙傝�冭建杩规牳瀹炩�� | 閫氳繃 |
+| 2 | 瀵煎嚭鎺掗櫎鎻愰啋鍨� | onlyVerify=true | RULE_004 鎻愰啋鍨嬩笉鍑虹幇 | 鏈嚭鐜� RULE_004 | 閫氳繃 |
+| 3 | 瀵煎嚭鐘舵�佽繃婊� | status=pending / handled | 浠呭緟澶勭悊 / 浠呭凡澶勭悊 | pending=475 琛岋紱handled 鏃犳暟鎹紙褰撳墠鏈熸棤宸插鐞嗚锛岃〃澶翠繚鐣欙級 | 閫氳繃 |
+| 4 | 鍚堝苟鍗曞厓鏍煎榻愭ā鏉� | openpyxl 妫�鏌� merged | 浼佷笟(B)涓庨棶棰�(G)璺ㄨ鍚堝苟 | 涓庢ā鏉� B2:B4/G2:G4 鍚屾瀯 | 閫氳繃 |
+| 5 | 鏁存湰棰勮 7 鏈� | preview?period=2026-07&types=summaryWorkbook | 璐ц繍/鍩庡競瀹㈣繍/涓彛寰勫綋鏈�+绱+鍚屾瘮琛� | 璐ц繍閲忓綋鏈� 16285.71 涓囧惃銆佺疮璁� 113423.13锛涘懆杞噺褰撴湀/绱/鍚屾瘮锛涘煄甯傚杩愰噺褰撴湀 30612.85 涓囦汉娆★紱涓彛寰勫綋鏈�/绱榻愬叏 | 閫氳繃 |
+| 6 | 鏁存湰棰勮缂� 2025 鍩烘湡鎻愮ず | 鍚� 5 | 鍚屾瘮缂烘暟鎹樉绀烘槑纭彁绀鸿�岄潪闈欓粯 | 璐ц繍閲�/鍩庡競瀹㈣繍/涓彛寰� 3 椤瑰潎鎻愮ず鈥�2025-07 鍚屾湡鏁版嵁鏈鍏ュ簱锛屽悓姣旀殏涓嶆樉绀衡�� | 閫氳繃 |
+| 7 | 鏁存湰棰勮 8 鏈堢己褰撴湀婧愭暟鎹� | preview?period=2026-08 | 缂哄綋鏈堟暟鎹椂鎻愮ず | 鎻愮ず缂� 2026-08 璐ц繍閲忓懆杞噺/瑙勪笂瑙勪笅褰撴湀锛岀疮璁″彇鏈�杩戝鍏ユ湀 | 閫氳繃 |
+| 8 | 鏁版嵁鏌ョ湅鍒楅殣钘� | /api/data/list?type=h2032 鍒楅泦鍚� | 鍓嶇闅愯棌 3 鍒� | 鏈嶅姟绔惈 enterpriseCode/unifiedCreditCode/reportUnit锛堝墠绔凡鎸� prop+label 杩囨护锛� | 閫氳繃锛堥〉闈㈠睍绀哄緟浜哄伐鐐归獙锛� |
+| 9 | 鎶ヨ〃椤垫ā鍧楀彲瑙佹�� | 鍓嶇浜х墿鎶芥煡 | 璐ц繍/鏃呭/鍩庡競瀹㈣繍闅愯棌 | 3 缁� hidden:true锛岄〉绛句粎 姹囨�诲ぇ琛�/鎶曡祫/鑳借�� | 閫氳繃 |
+| 10 | 缂栬瘧/杩愯 | mvn compile + 閲嶅惎 | 鏃犵紪璇戦敊璇� | EXIT=0锛屾湇鍔″氨缁� | 閫氳繃 |
+
+## 鍥涖�佸彂鐜伴棶棰樹笌澶勭疆
+- 瀵煎嚭銆屼紒涓氬悎骞躲�嶅師瀹炵幇鍚堝苟鎶ヨ〃鏈熷垪鑰岄潪浼佷笟鍒楋紝涓庢ā鏉夸笉绗� 鈫� 宸蹭慨姝d负 B锛堜紒涓氾級+G锛堥棶棰橈級鍚堝苟銆�
+- 瀵煎嚭鐘舵�佹湭闅忕晫闈㈢瓫閫� 鈫� 鍚庣鍙傛暟鐢� handledOnly 鏀逛负 status=all/pending/handled锛屽墠绔悓姝ヤ紶鍙傘��
+- 棰勮鍚屾瘮鍥犲簱鍐呮棤 2025-07 鍩烘湡鏁版嵁鑰岀己琛岋紙璐ц繍閲�/鍩庡競瀹㈣繍/涓彛寰勶級鈫� 澧炲姞閫愰」鎻愮ず鏂囨锛屾槑纭�滃緟 2025 骞村畾绋挎暟鎹鍏ュ悗鑷姩鍑虹幇鈥濓紱鏁存湰瀹為檯鐢熸垚浠嶄互姣嶇増 2025 骞村垪鍏紡涓哄噯銆�
+- 鏁版嵁鏌ョ湅绫诲瀷鏂囨涓庡鍏ラ〉涓嶄竴鑷达紙H203-1鏃呭鏈堟姤 vs 鍏矾鏃呭杩愯緭鏈堝害鐢熶骇鎯呭喌绛夛級鈫� 宸插榻愬鍏ラ〉鏂囨锛坴alue 娌跨敤鏁版嵁鏌ョ湅鍚庣鏀寔 key锛夈��
+
+## 浜斻�佸緟涓氬姟鐐归獙 / 閬楃暀
+- 涓変釜椤甸潰锛堝鏍哥粨鏋溿�佹暟鎹煡鐪嬨�佹姤琛ㄧ敓鎴愶級寤鸿鍦ㄦ祻瑙堝櫒閫愰〉鐐逛竴閬嶏細绛涢�夎仈鍔ㄣ�佽〃鏍煎垪灞曠ず銆佸鍑烘枃浠舵墦寮�鏍稿涓庢ā鏉块�愬垪姣斿銆�
+- 鏁版嵁鏌ョ湅褰撳墠浠呭紑鏀惧悗绔凡鏀寔鐨勭被鍨嬶紙璐ц繍 H2032/杩愭斂/杞ㄨ抗/瑙勪笂瑙勪笅/璐ц繍閲忓懆杞噺銆佸叕璺梾瀹� H2031/鏃呭杩愭斂銆佸叕浜�/鍑虹/鍑虹杩愭斂锛夛紱涓綋瀹㈣繍閲忓懆杞噺銆佺綉绾﹁溅鎬绘暟/璁㈠崟銆佹姇璧勩�佽兘鑰楃瓑瀵煎叆绫诲瀷鐨勬暟鎹煡鐪嬪緟鍚庣画纭鏄惁寮�鏀俱��
+- 鏁存湰棰勮鐨勫悓姣旇渚濊禆 2025 骞村熀鏈熸暟鎹叆搴擄紱姝ゅ墠绾﹀畾鐨勨�滃悓姣斾互姣嶇増缂撳瓨浼樺厛锛圡2 SummaryFiller锛夆�濇敼閫犱粛鏈惎鍔ㄣ��
+
+## 鍏�佺粨璁�
+鏈 4 椤圭晫闈�/瀵煎嚭鏁存敼宸茶惤鍦板苟閫氳繃鎺ュ彛绾т笌浜х墿绾ч獙璇侊紱椤甸潰瑙嗚涓庝氦浜掑缓璁笟鍔″湪娴忚鍣ㄧ粓楠屽悗鍗冲彲浣滀负浠婃棩浜や粯鐨勪竴閮ㄥ垎銆�
diff --git "a/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-09.md" "b/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-09.md"
index 63d249e..705edab 100644
--- "a/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-09.md"
+++ "b/docs/\345\267\245\344\275\234\346\227\245\347\273\223/\345\267\245\344\275\234\346\227\245\347\273\223_2026-09-09.md"
@@ -22,3 +22,19 @@
 
 ## 鏄庢棩璁″垝
 - 鍚姩 M2 SummaryFiller锛堝厛 7 鏈堝洖褰� diff=0锛屽啀 8 鏈堝疄寮癸級锛岃ˉ榻� 8 鏈堣揣杩愰噺鍛ㄨ浆閲�/瑙勪笂瑙勪笅/涓綋瀹㈣繍绛夌己鏁板悗鏁撮摼楠岃瘉銆�
+
+
+---
+
+## 涓婂崍绗簩杞紙4 椤规暣鏀癸級2026-09-09 11:xx
+### 瀹屾垚鍐呭
+- 瀹℃牳缁撴灉椤碉細鏄庣粏瑙嗗浘鏀逛负涓庣敤鎴锋ā鏉垮悓娆剧殑 10 鍒楄〃鏍硷紙鎶ヨ〃鏈�/浼佷笟/瑙勫垯缂栫爜/瑙勫垯/绛夌骇/瀹為檯/闃堝��/鍋忓樊/鐘舵��/鎿嶄綔锛夛紝鍗$墖寮忕Щ闄わ紱鏂板銆岄棶棰樼被鍨嬶紙鏍稿疄鍨嬮粯璁�/鍏ㄩ儴锛夈�嶄笌銆岀姸鎬侊紙鍏ㄩ儴/寰呭鐞�/宸插鐞嗭級銆嶇瓫閫夛紱瀵煎嚭鎶婁袱椤圭瓫閫夊甫缁欏悗绔��
+- 瀹℃牳瀵煎嚭锛堟ā鏉� docs/瀹℃牳鐣岄潰鏈堟姤妯℃澘/妯℃澘_瀹℃牳缁撴灉_.xlsx 鍙� Sheet锛夛細Sheet1 瀹℃牳缁撴灉锛堜粎鏍稿疄鍨嬶紝鎺掗櫎 RULE_004 鎻愰啋鍨嬶級锛汼heet2 浼佷笟鍚堝苟鎸変紒涓氭眹鎬伙紝闂鍒楄嚜鍔ㄧ敓鎴� 1銆�2銆�3 缂栧彿璇濇湳锛屼紒涓�/闂涓ゅ垪鎸夋ā鏉垮悎骞跺崟鍏冩牸锛堟鍓嶉敊鍚堝苟鎶ヨ〃鏈熷垪宸蹭慨姝o級銆�
+- 鏁版嵁鏌ョ湅锛氭暟鎹被鍨嬮�夋嫨鏀逛负銆屽垎缁勬ā鍧�+绫诲瀷涓嬫媺銆嶄笌瀵煎叆椤靛悓娆惧竷灞�涓庢枃妗堬紱鎶ヨ〃鏈熴�佸叧閿瓧鏌ヨ鏀惧埌涓嬫柟绛涢�夎锛涗紒涓氫唬鐮�/缁熶竴绀句細淇$敤浠g爜/濉姤鍗曚綅涓夊垪闅愯棌锛堟寜 prop 涓� label 鍙岄噸杩囨护锛夈��
+- 鎶ヨ〃鐢熸垚锛氳揣杩�/鏃呭/鍩庡競瀹㈣繍涓変釜灏忚〃妯″潡鏁寸粍闅愯棌锛岄〉绛句粎鍓� 姹囨�诲ぇ琛�/鎶曡祫鎶ヨ〃/鑳借�楁姤琛紱閬撹矾杩愯緭閲忔眹鎬昏〃锛堟暣鏈級棰勮鏂板鍏抽敭鎸囨爣锛氳揣杩愰噺銆佽揣杩愬懆杞噺銆佸煄甯傚杩愰噺/鍛ㄨ浆閲忋�佷腑鍙e緞瀹㈣繍閲�/鍛ㄨ浆閲忕殑褰撴湀/1..M绱/鍚屾瘮锛堟暟鎹綈鍗虫樉绀猴紝缂� 2025 骞村熀鏈熸暟鎹椂閫愰」缁欐彁绀猴級銆�
+
+### 娴嬭瘯缁撴灉鎽樿
+- mvn 缂栬瘧閫氳繃锛涘悗绔噸鍚氨缁紱鍓嶇 dev 缂栬瘧鎴愬姛锛坈hunk 鎶芥煡纭鏂板弬鏁板凡杩涗骇鐗╋級銆�
+- /api/audit/export 2026-08 H2032锛氬鏍哥粨鏋� 475 琛� 10 鍒� + 浼佷笟鍚堝苟 150 缁勶紙B/G 鍚堝苟锛夛紝浠呮牳瀹炲瀷锛泂tatus=pending/handled 杩囨护鐢熸晥銆�
+- /api/report/preview summaryWorkbook锛�2026-07 鏄剧ず鍏ㄧ渷璐ц繍閲� 16285.71 涓囧惃/绱 113423.13锛屽懆杞噺褰撴湀/绱+鍚屾瘮锛屽煄甯傚杩愰噺褰撴湀 30612.85 涓囦汉娆$瓑锛涘悓姣旂己 2025-07 鍩烘湡鏁版嵁鐨勪笁椤瑰潎鏈夋槑纭彁绀恒��
+- 鏁版嵁鏌ョ湅鍒楃粨鏋勬牳瀵癸細鏈嶅姟绔惈 enterpriseCode/unifiedCreditCode/reportUnit 涓夊垪锛堝墠绔殣钘忕敓鏁堬級銆�
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/controller/AuditController.java b/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/controller/AuditController.java
index dd04fa2..2608e84 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/controller/AuditController.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/controller/AuditController.java
@@ -56,8 +56,10 @@
 
     @GetMapping("/export")
     public ResponseEntity<byte[]> exportResults(@RequestParam("period") String period,
-                                                @RequestParam(value = "reportType", defaultValue = "H2032") String reportType) throws Exception {
-        byte[] bytes = auditService.exportResults(period, reportType);
+                                                @RequestParam(value = "reportType", defaultValue = "H2032") String reportType,
+                                                @RequestParam(value = "onlyVerify", defaultValue = "true") boolean onlyVerify,
+                                                @RequestParam(value = "status", defaultValue = "all") String status) throws Exception {
+        byte[] bytes = auditService.exportResults(period, reportType, onlyVerify, status);
         String name = java.net.URLEncoder.encode("瀹℃牳缁撴灉_" + period + ".xlsx", "UTF-8");
         return ResponseEntity.ok()
             .header("Content-Disposition", "attachment; filename*=UTF-8''" + name)
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java b/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java
index 8269c27..75c05b2 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java
@@ -1403,24 +1403,29 @@
         return a;
     }
 
-    /** 瀹℃牳缁撴灉瀵煎嚭 Excel锛堝惈浼佷笟鍚�/瑙勫垯鍚�/鐘舵��/瀹℃牳鎰忚锛� */
+    /** 瀹℃牳缁撴灉瀵煎嚭 Excel锛堜袱 Sheet锛屾寜涓氬姟妯℃澘锛夛細Sheet1 瀹℃牳缁撴灉锛堥�愭潯鏄庣粏锛夛紱Sheet2 浼佷笟鍚堝苟锛堟寜浼佷笟姹囨�� + 闂鍒楋級 */
     public byte[] exportResults(String reportPeriod, String reportType) throws Exception {
+        return exportResults(reportPeriod, reportType, true, "all");
+    }
+
+    /** onlyVerify=true 鏃跺墧闄も�滄彁閱掑瀷鈥濊鍒欙紙濡� RULE_004 鐜瘮鎻愰啋锛夛紝鍙繚鐣欐牳瀹炲瀷闂锛泂tatus=all/pending/handled 涓庣晫闈⑩�滅姸鎬佲�濈瓫閫変竴鑷� */
+    public byte[] exportResults(String reportPeriod, String reportType, boolean onlyVerify, String status) throws Exception {
         List<AuditResult> results = queryResults(reportPeriod, reportType);
+        java.util.List<AuditResult> filtered = new java.util.ArrayList<>();
+        String st = status == null ? "all" : status.trim();
+        for (AuditResult res : results) {
+            if (onlyVerify && isReminderRule(res.getRuleCode())) continue;
+            if ("pending".equals(st) && !"PENDING".equals(res.getStatus())) continue;
+            if ("handled".equals(st) && (res.getStatus() == null || "PENDING".equals(res.getStatus()))) continue;
+            filtered.add(res);
+        }
         try (XSSFWorkbook wb = new XSSFWorkbook()) {
+            // ---------- Sheet1 瀹℃牳缁撴灉锛堥�愭潯锛屽垪涓庝笟鍔℃ā鏉夸竴鑷达級 ----------
             Sheet sheet = wb.createSheet("瀹℃牳缁撴灉");
             String[] headers = {"鎶ヨ〃鏈�", "浼佷笟/甯傚窞", "瑙勫垯缂栫爜", "瀹℃牳瑙勫垯", "绛夌骇", "瀹為檯鍊�", "闃堝��/鍩哄噯", "鍋忓樊璇存槑", "鐘舵��", "瀹℃牳鎰忚"};
-            Row head = sheet.createRow(0);
-            for (int i = 0; i < headers.length; i++) {
-                Cell cell = head.createCell(i);
-                cell.setCellValue(headers[i]);
-                org.apache.poi.ss.usermodel.CellStyle style = wb.createCellStyle();
-                org.apache.poi.ss.usermodel.Font font = wb.createFont();
-                font.setBold(true);
-                style.setFont(font);
-                cell.setCellStyle(style);
-            }
+            writeAuditHeader(sheet, headers);
             int r = 1;
-            for (AuditResult res : results) {
+            for (AuditResult res : filtered) {
                 Row row = sheet.createRow(r++);
                 row.createCell(0).setCellValue(valueOf(res.getReportPeriod()));
                 row.createCell(1).setCellValue(valueOf(res.getEnterpriseName()));
@@ -1437,12 +1442,97 @@
             for (int i = 0; i < widths.length; i++) {
                 sheet.setColumnWidth(i, widths[i] * 256);
             }
+
+            // ---------- Sheet2 浼佷笟鍚堝苟锛堝悓涓�浼佷笟澶氭潯瑙勫垯骞舵帓鍒楀嚭 + 闂姹囨�诲垪锛� ----------
+            Sheet ms = wb.createSheet("浼佷笟鍚堝苟");
+            String[] h2 = {"鎶ヨ〃鏈�", "浼佷笟/甯傚窞", "瀹℃牳瑙勫垯", "瀹為檯鍊�", "闃堝��/鍩哄噯", "鍋忓樊璇存槑", "闂"};
+            writeAuditHeader(ms, h2);
+            java.util.LinkedHashMap<String, java.util.List<AuditResult>> groups = new java.util.LinkedHashMap<>();
+            for (AuditResult res : filtered) {
+                String key = valueOf(res.getEnterpriseName());
+                if (key.isEmpty()) key = valueOf(res.getReportPeriod()) + "#" + valueOf(res.getActualValue());
+                groups.computeIfAbsent(key, k -> new java.util.ArrayList<>()).add(res);
+            }
+            int rowCursor = 1;
+            for (java.util.Map.Entry<String, java.util.List<AuditResult>> e : groups.entrySet()) {
+                java.util.List<AuditResult> items = e.getValue();
+                int groupStart = rowCursor;
+                StringBuilder problems = new StringBuilder();
+                int seq = 0;
+                for (AuditResult it : items) {
+                    Row row = ms.createRow(rowCursor++);
+                    if (seq == 0) {
+                        row.createCell(0).setCellValue(valueOf(it.getReportPeriod()));
+                        row.createCell(1).setCellValue(valueOf(it.getEnterpriseName()).isEmpty() ? e.getKey() : valueOf(it.getEnterpriseName()));
+                    }
+                    row.createCell(2).setCellValue(valueOf(it.getRuleName()));
+                    row.createCell(3).setCellValue(valueOf(it.getActualValue()));
+                    row.createCell(4).setCellValue(valueOf(it.getThresholdValue()));
+                    row.createCell(5).setCellValue(valueOf(it.getDeviation()));
+                    seq++;
+                    if (problems.length() > 0) problems.append("\n");
+                    problems.append(seq).append("銆�").append(issueSentence(it));
+                }
+                if (rowCursor - groupStart > 1) {
+                    ms.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(groupStart, rowCursor - 1, 1, 1));
+                    ms.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(groupStart, rowCursor - 1, 6, 6));
+                }
+                Row first = ms.getRow(groupStart);
+                if (first.getCell(6) == null) first.createCell(6);
+                first.getCell(6).setCellValue(problems.toString());
+            }
+            int[] widths2 = {12, 30, 22, 34, 22, 34, 60};
+            for (int i = 0; i < widths2.length; i++) {
+                ms.setColumnWidth(i, widths2[i] * 256);
+            }
             ByteArrayOutputStream out = new ByteArrayOutputStream();
             wb.write(out);
             return out.toByteArray();
         }
     }
 
+    private void writeAuditHeader(Sheet sheet, String[] headers) {
+        Row head = sheet.createRow(0);
+        for (int i = 0; i < headers.length; i++) {
+            Cell cell = head.createCell(i);
+            cell.setCellValue(headers[i]);
+            org.apache.poi.ss.usermodel.CellStyle style = sheet.getWorkbook().createCellStyle();
+            org.apache.poi.ss.usermodel.Font font = sheet.getWorkbook().createFont();
+            font.setBold(true);
+            style.setFont(font);
+            cell.setCellStyle(style);
+        }
+    }
+
+    /** 鎻愰啋鍨嬭鍒欙細浠呮彁绀哄彉鍖栥�佷笉闇�浜哄伐鏍稿疄锛堝 RULE_004 璐ц繍绫诲瀷鐜瘮鎻愰啋锛� */
+    private boolean isReminderRule(String ruleCode) {
+        return "RULE_004".equals(ruleCode);
+    }
+
+    /** 浼佷笟鍚堝苟鈥滈棶棰樷�濆垪璇濇湳锛氭寜瑙勫垯绫诲瀷鐢熸垚鍙鐨勬牳瀹炴剰瑙� */
+    private String issueSentence(AuditResult res) {
+        String code = res.getRuleCode();
+        String dev = valueOf(res.getDeviation());
+        String act = valueOf(res.getActualValue());
+        String thr = valueOf(res.getThresholdValue());
+        if ("RULE_001".equals(code) || "RULE_002".equals(code)) {
+            return "浼佷笟涓婃姤杞﹁締/鍚ㄤ綅涓庤繍鏀挎暟鎹姣斿瓨鍦ㄥ樊寮傦紙" + dev + "锛夛紝璇锋牳瀹�";
+        }
+        if ("RULE_003".equals(code)) {
+            return "涓婃姤鍛ㄨ浆閲忎笌杞ㄨ抗娴嬬畻鍛ㄨ浆閲忓樊寮傛槑鏄撅紙" + dev + "锛夛紝璇峰弬鑰冭建杩规牳瀹炰紒涓氬懆杞噺";
+        }
+        if ("RULE_004".equals(code)) {
+            return "璐ц繍绫诲瀷鐜瘮鍙戠敓鍙樺寲锛岃鏍稿疄";
+        }
+        if ("RULE_005".equals(code)) {
+            return "鏃犻泦瑁呯杞︿絾瀛樺湪闆嗚绠辫揣杩愰噺锛岃鏍稿疄杞﹁締绫诲瀷涓庤揣杩愰噺濉姤";
+        }
+        if ("RULE_006".equals(code)) {
+            return "鏁磋溅鍚ㄤ綅瓒呭嚭 4~40 鍚ㄥ悎鐞嗚寖鍥达紝璇锋牳瀹炲钩鍧囧惃浣嶅~鎶�";
+        }
+        return valueOf(res.getRuleName()) + "寮傚父锛堝疄闄咃細" + act + "锛涘熀鍑嗭細" + thr + "锛�" + dev + "锛夛紝璇锋牳瀹�";
+    }
+
     private String auditStatusCn(String status) {
         if ("CONFIRMED".equals(status)) return "宸茬‘璁�";
         if ("IGNORED".equals(status)) return "宸插拷鐣�";
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
index 9c502da..7fa99e7 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -334,12 +334,7 @@
                     if (resolveSummaryMother(period, mode) == null) {
                         problems.add("docs/鐢熸垚姹囨�诲ぇ琛� 涓嬬己灏戝彲澶嶅埗鐨勬瘝鐗堟眹鎬昏〃");
                     } else {
-                        Map<Integer, Map<String, double[]>> busM = loadCityBusByMonth(yearPrefix, month);
-                        Map<Integer, Map<String, double[]>> taxiM = loadCityTaxiByMonth(yearPrefix, month);
-                        Map<String, double[]> bc = busM.get(month);
-                        Map<String, double[]> tc = taxiM.get(month);
-                        if (bc != null && bc.get("鍏ㄧ渷") != null) addSum(summary, "鍩庡競鍏氦瀹㈣繍閲忥紙鍏ㄧ渷褰撴湀锛�", bc.get("鍏ㄧ渷")[0], "涓囦汉");
-                        if (tc != null && tc.get("鍏ㄧ渷") != null) addSum(summary, "宸℃父鍑虹瀹㈣繍閲忥紙鍏ㄧ渷褰撴湀锛�", tc.get("鍏ㄧ渷")[0], "涓囦汉");
+                        summaryWorkbookPreview(period, yearNum, month, summary, problems);
                     }
                 } catch (Exception ex) {
                     problems.add(ex.getMessage());
@@ -443,6 +438,118 @@
         return sb.toString();
     }
 
+    /** 姹囨�绘暣鏈叧閿寚鏍囬瑙堬細璐ц繍閲�/鍛ㄨ浆閲忋�佸煄甯傚杩愩�佷腑鍙e緞鐨� 褰撴湀銆�1..M 绱銆佸綋鏈堝悓姣斻�佺疮璁″悓姣旓紙鍚屾瘮鍙栧簱鍐呭幓骞村悓鏈燂紝缂哄垯鏄剧ず鈥旓級 */
+    private void summaryWorkbookPreview(String period, int year, int month,
+                                        List<Map<String, Object>> summary, List<String> problems) {
+        String yearPrefix = year + "-";
+        // ---- 璐ц繍锛堥噺=妯℃澘_璐ц繍閲忓懆杞噺鍏ㄧ渷琛岋紱鍛ㄨ浆閲�=瑙勪笂瑙勪笅鎷嗗垎琛ㄥ叏鐪佽锛� ----
+        FreightTurnoverImport ft = loadFreightTurnover(period).get("婀栧寳鐪�");
+        Double fm = ft == null ? null : freightMonth(ft, month);
+        Double fcum = ft == null ? null : freightCum(ft, month);
+        Double fy = ft == null ? null : freightYoy(ft, month);
+        Double fcy = ft == null ? null : freightCumYoy(ft, month);
+        addKpi(summary, "鍏ㄧ渷璐ц繍閲�", fm, fcum, fy, fcy, "涓囧惃");
+        if (fm != null && fy == null) {
+            problems.add((year - 1) + "-" + String.format("%02d", month) + " 鍚屾湡璐ц繍閲忔暟鎹湭瀵煎叆搴擄紝璐ц繍閲忓綋鏈�/绱鍚屾瘮鏆備笉鏄剧ず锛屽緟 2025 骞村畾绋挎暟鎹鍏ュ悗鑷姩鍑虹幇");
+        }
+        ScaleSplitTransport tm = loadProvinceMonthMap(period).get(month);
+        ScaleSplitTransport tc = getProvinceCumulative(period);
+        Double tvM = tm == null ? null : tm.getTotalTurnover();
+        Double tvC = tc == null ? null : tc.getTotalTurnover();
+        Double tvMY = tm == null ? null : getYoyMetric(tm, "turnover", "total");
+        Double tvCY = tc == null ? null : getYoyMetric(tc, "turnover", "total");
+        addKpi(summary, "鍏ㄧ渷璐х墿鍛ㄨ浆閲�", tvM, tvC, tvMY, tvCY, "涓囧惃鍏噷");
+        if (fm == null) problems.add("妯℃澘_璐ц繍閲忓懆杞噺缂� " + period + " 褰撴湀鏁版嵁锛岃揣杩愰噺浠呰兘鎻愪緵鎴嚦鏈�杩戝鍏ユ湀鐨勭疮璁�");
+        if (tvM == null) problems.add("瑙勪笂瑙勪笅鎷嗗垎缂� " + period + " 褰撴湀鏁版嵁锛屽懆杞噺/瑙勪笂瑙勪笅褰撴湀缂猴紝绱涓烘埅鑷虫渶杩戝鍏ユ湀");
+
+        // ---- 鍩庡競瀹㈣繍锛堝叕浜�+鍑虹+杞ㄩ亾+杞浮 鍏ㄧ渷灏忚锛涙寚鏍囦綅 0/2/4/6=瀹㈣繍锛�1/3/5/7=鍛ㄨ浆锛� ----
+        double[] cumM = cityCumProv(yearPrefix, month);
+        double[] cumPrev = cityCumProv(yearPrefix, month - 1);
+        double[] monthVec = diff8(cumM, cumPrev);
+        double cityPaxM = sumIdx(monthVec, new int[]{0, 2, 4, 6});
+        double cityTurnM = sumIdx(monthVec, new int[]{1, 3, 5, 7});
+        double cityPaxC = sumIdx(cumM, new int[]{0, 2, 4, 6});
+        double cityTurnC = sumIdx(cumM, new int[]{1, 3, 5, 7});
+        double[] lastCumM = cityCumProv((year - 1) + "-", month);
+        double[] lastCumPrev = cityCumProv((year - 1) + "-", month - 1);
+        double[] lastVec = diff8(lastCumM, lastCumPrev);
+        double lastPaxM = sumIdx(lastVec, new int[]{0, 2, 4, 6});
+        double lastPaxC = sumIdx(lastCumM, new int[]{0, 2, 4, 6});
+        double lastTurnM = sumIdx(lastVec, new int[]{1, 3, 5, 7});
+        double lastTurnC = sumIdx(lastCumM, new int[]{1, 3, 5, 7});
+        addKpiRaw(summary, "鍩庡競瀹㈣繍閲�", cityPaxM, cityPaxC, lastPaxM, lastPaxC, "涓囦汉娆�");
+        addKpiRaw(summary, "鍩庡競瀹㈣繍鍛ㄨ浆閲�", cityTurnM, cityTurnC, lastTurnM, lastTurnC, "涓囦汉鍏噷");
+
+        // ---- 涓彛寰勶紙鍏矾鏃呭浼佷笟 H203-1 鍏ㄧ渷锛屽崟浣嶆崲绠� /10000锛� ----
+        Map<Integer, Map<String, PassengerAgg>> data = loadPassengerAggMap();
+        double paxM = 0.0, paxC = 0.0, turnM = 0.0, turnC = 0.0;
+        double lastPaxM2 = 0.0, lastPaxC2 = 0.0, lastTurnM2 = 0.0, lastTurnC2 = 0.0;
+        for (int m = 1; m <= month; m++) {
+            PassengerAgg agg = aggOf(data, year, m, "婀栧寳鐪�");
+            if (agg != null) {
+                double pax = agg.passengerTotal / 10000.0;
+                double turn = agg.turnoverTotal / 10000.0;
+                if (m == month) { paxM = pax; turnM = turn; }
+                paxC += pax;
+                turnC += turn;
+            }
+            PassengerAgg last = aggOf(data, year - 1, m, "婀栧寳鐪�");
+            if (last != null) {
+                double lp = last.passengerTotal / 10000.0;
+                double lt = last.turnoverTotal / 10000.0;
+                if (m == month) { lastPaxM2 = lp; lastTurnM2 = lt; }
+                lastPaxC2 += lp;
+                lastTurnC2 += lt;
+            }
+        }
+        addKpiRaw(summary, "涓彛寰勫杩愰噺", paxM, paxC, lastPaxM2, lastPaxC2, "涓囦汉娆�");
+        addKpiRaw(summary, "涓彛寰勫懆杞噺", turnM, turnC, lastTurnM2, lastTurnC2, "涓囦汉鍏噷");
+
+        if (paxM <= 0 && paxC <= 0 && cityPaxC <= 0 && fm == null) {
+            problems.add("璐ц繍閲忓懆杞噺/瑙勪笂瑙勪笅/鍏矾鏃呭/鍩庡競瀹㈣繍褰撴湀鏁版嵁鍧囩己澶�");
+        }
+        if (cityPaxC > 0 && lastPaxC <= 0) {
+            problems.add((year - 1) + "-" + String.format("%02d", month) + " 鍚屾湡鍩庡競瀹㈣繍鏁版嵁鏈鍏ュ簱锛屽煄甯傚杩愬悓姣旀殏涓嶆樉绀猴紝寰� 2025 骞村畾绋挎暟鎹鍏ュ悗鑷姩鍑虹幇");
+        }
+        if (paxC > 0 && lastPaxC2 <= 0) {
+            problems.add((year - 1) + "-" + String.format("%02d", month) + " 鍚屾湡鍏矾鏃呭鏁版嵁鏈鍏ュ簱锛屼腑鍙e緞鍚屾瘮鏆備笉鏄剧ず锛屽緟 2025 骞村畾绋挎暟鎹鍏ュ悗鑷姩鍑虹幇");
+        }
+    }
+
+    /** 鍩庡競瀹㈣繍鍏ㄧ渷绱鍚戦噺锛�8 鎸囨爣锛氬叕浜ゅ杩�/鍛ㄨ浆銆佸嚭绉熷杩�/鍛ㄨ浆銆佽建閬撳杩�/鍛ㄨ浆銆佽疆娓″杩�/鍛ㄨ浆锛� */
+    private double[] cityCumProv(String yearPrefix, int month) {
+        if (month <= 0) return new double[8];
+        return loadCityPassengerCumulative(yearPrefix, month).getOrDefault("鍏ㄧ渷", new double[8]);
+    }
+
+    private double[] diff8(double[] a, double[] b) {
+        double[] out = new double[8];
+        for (int i = 0; i < 8; i++) out[i] = a[i] - b[i];
+        return out;
+    }
+
+    private double sumIdx(double[] arr, int[] idx) {
+        double sum = 0.0;
+        for (int i : idx) sum += (i < arr.length ? arr[i] : 0.0);
+        return sum;
+    }
+
+    /** 鐧惧垎姣斿悓姣旓紙鍊煎凡涓烘瘮鐜� 0.xx锛夛細杈撳嚭鍥涜 褰撴湀/绱/褰撴湀鍚屾瘮/绱鍚屾瘮 */
+    private void addKpi(List<Map<String, Object>> summary, String name, Double m, Double cum, Double yoyM, Double yoyC, String unit) {
+        if (m != null) addSum(summary, name + "路褰撴湀", round(m, 2), unit);
+        if (cum != null) addSum(summary, name + "路绱", round(cum, 2), unit);
+        if (yoyM != null) addSum(summary, name + "路褰撴湀鍚屾瘮", round(yoyM * 100.0, 1), "%");
+        if (yoyC != null) addSum(summary, name + "路绱鍚屾瘮", round(yoyC * 100.0, 1), "%");
+    }
+
+    /** 鍘熷鏁板�肩増锛堟湀/绱涓� 0 瑙嗕负鏃犳暟鎹笉鍑鸿锛� */
+    private void addKpiRaw(List<Map<String, Object>> summary, String name, double m, double cum, double lastM, double lastC, String unit) {
+        if (m > 0) addSum(summary, name + "路褰撴湀", round(m, 2), unit);
+        if (cum > 0) addSum(summary, name + "路绱", round(cum, 2), unit);
+        if (m > 0 && lastM > 0) addSum(summary, name + "路褰撴湀鍚屾瘮", round((m - lastM) / lastM * 100.0, 1), "%");
+        if (cum > 0 && lastC > 0) addSum(summary, name + "路绱鍚屾瘮", round((cum - lastC) / lastC * 100.0, 1), "%");
+    }
+
     private void addSum(List<Map<String, Object>> summary, String label, Double value, String unit) {
         Map<String, Object> m = new LinkedHashMap<>();
         m.put("label", label);
diff --git a/traffic-audit-web/src/views/AuditResult.vue b/traffic-audit-web/src/views/AuditResult.vue
index 7c510d5..f4b7278 100644
--- a/traffic-audit-web/src/views/AuditResult.vue
+++ b/traffic-audit-web/src/views/AuditResult.vue
@@ -59,6 +59,19 @@
               <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>
+          </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="瑙嗗浘">
             <el-radio-group v-model="viewMode">
               <el-radio label="detail">鎸夋潯鏄庣粏</el-radio>
@@ -98,45 +111,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="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>
+          <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>
-          <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%"
@@ -334,7 +354,7 @@
   data() {
     return {
       period: '', reportType: 'H2032', loading: false, marking: false, results: [], ruleStats: [], statsCollapsed: false,
-      enterpriseFilter: '', ruleFilter: '', viewMode: 'merged', batchSelection: [], focusMode: false,
+      enterpriseFilter: '', ruleFilter: '', viewMode: 'detail', issueScope: 'verify', statusScope: 'all', batchSelection: [], focusMode: false,
       explainDrafts: {}, savingExplainId: null, expandedExplain: null,
       monthVisible: false, monthLoading: false, monthType: '', monthPeriod: '', monthKeyword: '', monthRecords: [], monthColumns: []
     }
@@ -364,13 +384,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
       })
     },
@@ -577,7 +604,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') {
diff --git a/traffic-audit-web/src/views/DataView.vue b/traffic-audit-web/src/views/DataView.vue
index 3dce26a..7ab9413 100644
--- a/traffic-audit-web/src/views/DataView.vue
+++ b/traffic-audit-web/src/views/DataView.vue
@@ -16,21 +16,18 @@
       </el-aside>
       <el-main>
         <h3>瀵煎叆鏁版嵁鏌ョ湅</h3>
-        <el-form :inline="true">
-          <el-form-item label="鏁版嵁绫诲瀷">
-            <el-radio-group v-model="type" @change="loadData">
-              <el-radio-button label="h2032">H203-2鏈堟姤</el-radio-button>
-              <el-radio-button label="transportAuth">杩愭斂杞﹁締</el-radio-button>
-              <el-radio-button label="trackMileage">杞ㄨ抗閲岀▼</el-radio-button>
-              <el-radio-button label="scaleSplit">瑙勪笂瑙勪笅鎷嗗垎</el-radio-button>
-              <el-radio-button label="freightTurnover">璐ц繍閲忓懆杞噺</el-radio-button>
-              <el-radio-button label="h2031">H203-1鏃呭鏈堟姤</el-radio-button>
-              <el-radio-button label="passengerAuth">鏃呭杩愭斂杞﹁締</el-radio-button>
-              <el-radio-button label="cityBus">鍩庡競鍏氦鏈堟姤</el-radio-button>
-              <el-radio-button label="cityTaxi">宸℃父鍑虹鏈堟姤</el-radio-button>
-              <el-radio-button label="cityTaxiAuth">鍑虹杞﹁繍鏀胯溅杈�</el-radio-button>
-            </el-radio-group>
-          </el-form-item>
+        <div class="type-row">
+          <span class="type-label">鏁版嵁绫诲瀷</span>
+          <el-radio-group v-model="activeModule" size="small" @change="onModuleChange">
+            <el-radio-button v-for="g in modules" :key="g.key" :label="g.key">{{ g.name }}</el-radio-button>
+          </el-radio-group>
+          <el-select v-model="type" size="small" style="width:300px" placeholder="閫夋嫨鏁版嵁绫诲瀷" @change="onTypeSelect">
+            <el-option-group :label="activeTypes.name">
+              <el-option v-for="t in activeTypes.types" :key="t.value" :label="t.label" :value="t.value"></el-option>
+            </el-option-group>
+          </el-select>
+        </div>
+        <el-form :inline="true" class="filter-row">
           <el-form-item label="鎶ヨ〃鏈�">
             <el-date-picker v-model="period" type="month" placeholder="鍏ㄩ儴" value-format="yyyy-MM" clearable style="width:140px" @change="loadData"></el-date-picker>
           </el-form-item>
@@ -48,7 +45,7 @@
         <div class="table-wrap" ref="tableWrap">
           <div class="table-area">
             <el-table :data="records" border v-loading="loading" size="small" style="width:100%" height="100%">
-              <el-table-column v-for="c in columns" :key="c.prop" :prop="c.prop" :label="c.label" :min-width="c.width" show-overflow-tooltip>
+              <el-table-column v-for="c in visibleColumns" :key="c.prop" :prop="c.prop" :label="c.label" :min-width="c.width" show-overflow-tooltip>
                 <template slot-scope="scope">
                   <span>{{ formatCell(scope.row[c.prop]) }}</span>
                 </template>
@@ -73,6 +70,31 @@
   name: 'DataView',
   data() {
     return {
+      modules: [
+        {
+          key: 'freight', name: '閬撹矾璐ц繍', types: [
+            { value: 'h2032', label: '閬撹矾璐х墿杩愯緭鏈堝害鐢熶骇鎯呭喌' },
+            { value: 'transportAuth', label: '杩愭斂杞﹁締' },
+            { value: 'trackMileage', label: '杞ㄨ抗閲岀▼' },
+            { value: 'scaleSplit', label: '瑙勪笂瑙勪笅鎷嗗垎' },
+            { value: 'freightTurnover', label: '璐ц繍閲忓懆杞噺' }
+          ]
+        },
+        {
+          key: 'passenger', name: '鍏矾鏃呭', types: [
+            { value: 'h2031', label: '鍏矾鏃呭杩愯緭鏈堝害鐢熶骇鎯呭喌' },
+            { value: 'passengerAuth', label: '杩愭斂杞﹁締鏁�' }
+          ]
+        },
+        {
+          key: 'city', name: '鍩庡競瀹㈣繍', types: [
+            { value: 'cityBus', label: '鍩庡競鍏叡浜ら�氭湀搴﹁繍钀ユ儏鍐�' },
+            { value: 'cityTaxi', label: '宸℃父鍑虹姹借溅杩愯惀鏈嶅姟鎯呭喌鏈堟姤' },
+            { value: 'cityTaxiAuth', label: '鍑虹杞﹁繍鏀胯溅杈�' }
+          ]
+        }
+      ],
+      activeModule: 'freight',
       type: 'h2032',
       period: '',
       keyword: '',
@@ -85,7 +107,28 @@
       loadingMore: false
     }
   },
+  computed: {
+    activeTypes() {
+      const g = this.modules.find(x => x.key === this.activeModule) || this.modules[0]
+      return g
+    },
+    visibleColumns() {
+      const hiddenProps = ['enterpriseCode', 'unifiedCreditCode', 'reportUnit']
+      const hiddenLabels = ['浼佷笟浠g爜', '缁熶竴绀句細淇$敤浠g爜', '濉姤鍗曚綅']
+      return (this.columns || []).filter(c =>
+        hiddenProps.indexOf(c.prop) < 0 && hiddenLabels.indexOf(c.label) < 0)
+    }
+  },
   methods: {
+    onModuleChange(key) {
+      const g = this.modules.find(x => x.key === key)
+      if (!g || !g.types.length) return
+      if (!g.types.some(t => t.value === this.type)) {
+        this.type = g.types[0].value
+      }
+      this.loadData()
+    },
+    onTypeSelect() { this.loadData() },
     loadData() {
       this.loading = true
       this.loadingMore = false
@@ -100,7 +143,7 @@
       }).then(res => {
         const d = res.data || {}
         this.records = d.records || []
-        this.columns = d.columns || []
+        this.columns = (d.columns || []).filter(c => ['enterpriseCode', 'unifiedCreditCode', 'reportUnit'].indexOf(c.prop) < 0 && ['浼佷笟浠g爜', '缁熶竴绀句細淇$敤浠g爜', '濉姤鍗曚綅'].indexOf(c.label) < 0)
         this.total = d.total || 0
         if (this.page > 1 && this.records.length === 0) {
           this.page = 1
@@ -149,7 +192,7 @@
         this.records = this.records.concat(addRows)
         this.page = nextPage
         if (d.total != null) this.total = d.total
-        if (d.columns && d.columns.length) this.columns = d.columns
+        if (d.columns && d.columns.length) this.columns = d.columns.filter(c => ['enterpriseCode', 'unifiedCreditCode', 'reportUnit'].indexOf(c.prop) < 0 && ['浼佷笟浠g爜', '缁熶竴绀句細淇$敤浠g爜', '濉姤鍗曚綅'].indexOf(c.label) < 0)
       }).catch(e => {
         if (e && e._toast) return
         this.$message.error('鍔犺浇鏇村澶辫触')
@@ -216,6 +259,9 @@
 </script>
 <style scoped>
 .el-header { background: #409EFF; color: white; line-height: 60px; font-size: 18px; }
+.type-row { margin-bottom: 10px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
+.type-label { color: #606266; font-weight: 600; }
+.filter-row { margin-bottom: 4px; }
 #app .table-wrap {
   display: flex;
   flex-direction: column;
diff --git a/traffic-audit-web/src/views/ReportExport.vue b/traffic-audit-web/src/views/ReportExport.vue
index 3bac892..2cbe51b 100644
--- a/traffic-audit-web/src/views/ReportExport.vue
+++ b/traffic-audit-web/src/views/ReportExport.vue
@@ -27,7 +27,7 @@
 
           <!-- 绗簩琛岋細鎶ヨ〃绫诲瀷鍒囨崲锛堣揣杩� / 鏃呭 / 鎶曡祫 / 鑳借�� / 鍩庡競瀹㈣繍锛夛紱el-radio-group 鏇夸唬 el-tabs锛岃閬� el-tabs 甯冨眬鏃犻檺寰幆锛堜笌 /import 淇涓�鑷达級 -->
           <el-radio-group v-model="activeKey" class="module-tabs">
-            <el-radio-button v-for="m in modules" :key="m.key" :label="m.key">
+            <el-radio-button v-for="m in visibleModules" :key="m.key" :label="m.key">
               {{ m.title }}<el-tag v-if="m.tag" size="mini" :type="m.tagType || 'info'" style="margin-left:6px">{{ m.tag }}</el-tag>
             </el-radio-button>
           </el-radio-group>
@@ -176,21 +176,21 @@
           ], checked: []
         },
         {
-          key: 'freight', title: '璐ц繍鎶ヨ〃', items: [
+          key: 'freight', title: '璐ц繍鎶ヨ〃', hidden: true, items: [
             { key: 'cityDetail', label: '璐ц繍閲忓垎甯傚窞鏄庣粏', fileName: '鐢熸垚_璐ц繍閲忓垎甯傚窞鏄庣粏.xlsx', status: null, statusNote: '' },
             { key: 'freightRank', label: '璐ц繍閲忔帓鍚�', fileName: '鐢熸垚_璐ц繍閲忔帓鍚�.xlsx', status: null, statusNote: '' },
             { key: 'turnoverRank', label: '鍛ㄨ浆閲忔帓鍚�', fileName: '鐢熸垚_鍛ㄨ浆閲忔帓鍚�.xlsx', status: null, statusNote: '' }
           ], checked: []
         },
         {
-          key: 'passenger', title: '鏃呭鎶ヨ〃', items: [
+          key: 'passenger', title: '鏃呭鎶ヨ〃', hidden: true, items: [
             { key: 'passengerCityDetail', label: '鍏矾鏃呭鍒嗗競宸�', fileName: '鐢熸垚_鍏矾鏃呭鍒嗗競宸�.xlsx', status: null, statusNote: '' },
             { key: 'passengerMidDetail', label: '涓彛寰勬槑缁�', fileName: '鐢熸垚_鍏矾鏃呭涓彛寰勬槑缁�.xlsx', status: null, statusNote: '' },
             { key: 'passengerMidRank', label: '涓彛寰勬帓鍚�', fileName: '鐢熸垚_鍏矾鏃呭涓彛寰勬帓鍚�.xlsx', status: null, statusNote: '' },
             { key: 'passengerMidAnalysis', label: '涓彛寰勫垎鏋�', fileName: '鐢熸垚_鍏矾鏃呭涓彛寰勫垎鏋�.xlsx', status: null, statusNote: '' }
           ], checked: []
         },        {
-          key: 'cityPassenger', title: '鍩庡競瀹㈣繍鎶ヨ〃', items: [
+          key: 'cityPassenger', title: '鍩庡競瀹㈣繍鎶ヨ〃', hidden: true, items: [
             { key: 'cityBusDetail', label: '鍩庡競鍏氦瀹㈣繍閲忓垎甯傚窞鏄庣粏', fileName: '鐢熸垚_鍩庡競鍏氦瀹㈣繍閲忓垎甯傚窞鏄庣粏.xlsx', status: null, statusNote: '' },
             { key: 'cityTaxiDetail', label: '宸℃父鍑虹瀹㈣繍閲忓垎甯傚窞鏄庣粏', fileName: '鐢熸垚_宸℃父鍑虹瀹㈣繍閲忓垎甯傚窞鏄庣粏.xlsx', status: null, statusNote: '' },
             { key: 'cityRailFerryDetail', label: '杞ㄩ亾杞浮瀹㈣繍閲忓垎甯傚窞鏄庣粏', fileName: '鐢熸垚_杞ㄩ亾杞浮瀹㈣繍閲忓垎甯傚窞鏄庣粏.xlsx', status: null, statusNote: '' },
@@ -223,6 +223,9 @@
       const s = sessionStorage.getItem('modules')
       if (!s) return []
       return s.split(',').filter(Boolean)
+    },
+    visibleModules() {
+      return this.modules.filter(m => !m.hidden)
     },
     curModule() {
       return this.modules.find(m => m.key === this.activeKey) || this.modules[0]
@@ -260,8 +263,8 @@
     }
   },
   created() {
-    const mine = this.myModules.filter(k => this.modules.some(m => m.key === k))
-    this.activeKey = (mine.length ? mine : this.modules.map(m => m.key))[0]
+    const mine = this.myModules.filter(k => this.visibleModules.some(m => m.key === k))
+    this.activeKey = (mine.length ? mine : this.visibleModules.map(m => m.key))[0]
     this.ensureCycle()
     this.refreshAll()
   },  methods: {

--
Gitblit v1.9.1