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