From 7858c25faa53dd2f4c0faae58618565ac5bb50ae Mon Sep 17 00:00:00 2001
From: zzw <228544128@qq.com>
Date: 星期三, 14 十月 2026 13:07:22 +0800
Subject: [PATCH] feat: add engineering order print and externalize app config

---
 requirement/改进2026.10.txt                                                                                    |   11 
 requirement/敏感词字典.md                                                                                         |    2 
 emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java                     |   22 
 emergency-repair-web/.env                                                                                    |   11 
 emergency-repair-web/src/views/fault/DispatchPage.vue                                                        |    4 
 requirement/决策表_待确认项.md                                                                                      |    4 
 emergency-repair-web/src/router/index.js                                                                     |   11 
 HANDOFF.md                                                                                                   |  297 +++++++
 emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java        |    4 
 emergency-repair-web/vue.config.js                                                                           |   19 
 emergency-repair-server/pom.xml                                                                              |   16 
 emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java | 1400 ++++++++++++++++++++++++++++++++++++
 emergency-repair-web/public/dict.js                                                                          |   12 
 emergency-repair-server/src/main/java/com/emergencyrepair/business/controller/FaultController.java           |   46 +
 emergency-repair-web/public/sensitive-words.json                                                             |   43 
 requirement/界面还原.html                                                                                        |    6 
 emergency-repair-web/src/views/Dashboard.vue                                                                 |    2 
 emergency-repair-web/src/views/fault/XcbReview.vue                                                           |   10 
 emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java              |   12 
 requirement/工程状态机设计.md                                                                                       |    6 
 requirement/前44页需求梳理.md                                                                                      |   10 
 requirement/交接文件_2026.10.3.md                                                                                |    2 
 emergency-repair-server/src/main/resources/application.yml                                                   |    4 
 emergency-repair-web/src/api/index.js                                                                        |    2 
 emergency-repair-web/src/views/fault/SimpleApproval.vue                                                      |   10 
 emergency-repair-web/src/utils/dict.js                                                                       |    8 
 emergency-repair-web/src/views/fault/EngineeringPrint.vue                                                    |  272 +++++++
 27 files changed, 2,159 insertions(+), 87 deletions(-)

diff --git a/HANDOFF.md b/HANDOFF.md
index a02bb1f..e16ae9b 100644
--- a/HANDOFF.md
+++ b/HANDOFF.md
@@ -404,12 +404,12 @@
 - 鏈細璇濊繘灞曪細鐢ㄦ埛缁欏嚭 **AXU1 娴佺▼鎬昏鐨勫彛寰勫彉鏇�**鈥斺�旂涓�鐗�**涓嶆寜鍘熷闇�姹�**锛屾敼涓� **鈶� 绠�鍖栦笟鍔℃祦绋� + 鈶� 鍙彁渚涚綉缁滅増銆佷笉鎻愪緵绂荤嚎鍗曟満鐗�**銆�
 - **绗竴鐗堢畝鍖栨祦绋嬶紙10 姝ワ紝宸茬‘璁わ紱2026-09-20 缁嗗寲楠屾敹閾撅級**锛歍鍛樻晠闅滀笂鎶� 鈫� XCB鍔╃悊瀹℃牳锛�**鍒嗛厤鎵夸慨鍗曚綅銆佹媶鍗曘�佺敓鎴愬伐绋嬪崟鍙�**锛夆啋 CMB棰嗗瀹℃壒 鈫� JCC鍔╃悊瀹℃壒 鈫� JCC澶勯暱瀹℃壒 鈫� XCB鍔╃悊娲惧崟锛�**鏄剧ず鏈淳鍗曟晠闅滐紝鍕鹃�夊悗涓�閿淳鍗曪紝鎸夋壙淇崟浣嶇敓鎴愭淳宸ュ崟**锛夆啋 鍘傚绾夸笅淇悊 鈫� 鍘傚涓婃姤瀹屽伐淇℃伅 鈫� 瀹屽伐楠屾敹娴佺▼锛堥獙鏀剁敵璇� 鈫� 璐ㄦ鍛橀獙鏀� 鈫� 璐ㄦ璐熻矗浜洪獙鏀� 鈫� 瀹屽伐楠屾敹鎵撳嵃杈撳嚭 鈫� 淇悗璐ㄩ噺璇勭骇锛夆啋 楠屾敹瀹屾垚銆�
 
-- 杩炲甫鍙e緞鍙樻洿锛氣憼 鍙栨秷鑹囬暱瀹℃壒鐜妭锛堝師寰呯‘璁ゆ竻鍗曠 9 鏉� 鈫� 宸茶В鍐筹細涓嶇撼鍏ョ涓�鐗堬級锛涒憽 鍙栨秷鍏ㄩ儴绂荤嚎鑳藉姏锛堝崟鏈虹増/绂荤嚎濉啓銆佹暟鎹寘瀵煎叆瀵煎嚭锛夛紱鈶� 鍙栨秷鈥淴CB鍔╃悊瀹屽伐澶勭悊/瀵煎嚭瀹屽伐椤圭洰鈥濈幆鑺傦紱鈶� 鎶ヤ环澶勭悊绉诲嚭涓绘祦绋嬶紱鈶� JCC鍔╃悊瀹℃壒纭涓哄繀缁忕幆鑺傘��
+- 杩炲甫鍙e緞鍙樻洿锛氣憼 鍙栨秷T闀垮鎵圭幆鑺傦紙鍘熷緟纭娓呭崟绗� 9 鏉� 鈫� 宸茶В鍐筹細涓嶇撼鍏ョ涓�鐗堬級锛涒憽 鍙栨秷鍏ㄩ儴绂荤嚎鑳藉姏锛堝崟鏈虹増/绂荤嚎濉啓銆佹暟鎹寘瀵煎叆瀵煎嚭锛夛紱鈶� 鍙栨秷鈥淴CB鍔╃悊瀹屽伐澶勭悊/瀵煎嚭瀹屽伐椤圭洰鈥濈幆鑺傦紱鈶� 鎶ヤ环澶勭悊绉诲嚭涓绘祦绋嬶紱鈶� JCC鍔╃悊瀹℃壒纭涓哄繀缁忕幆鑺傘��
 - 宸插悓姝ヨ惤鐩橈細`requirement\鐣岄潰杩樺師.html`锛堟柊澧炲叏灞�鍙e緞鏉� + AXU1 鍗$墖閲嶅啓 + AXU2/AXU13 鍙楀奖鍝嶆爣娉級銆乣requirement\鍓�44椤甸渶姹傛⒊鐞�.md`锛堢 2 鑺備笟鍔℃�昏銆�3.3 娴佺▼鎬昏銆�4.1/6.2/6.3/绗� 7 鑺傛爣娉ㄣ�佺 9 鑺傛柊澧� 14鈥�17 鏉°�佹湳璇〃锛夈�乣requirement\宸ョ▼鐘舵�佹満璁捐.md`锛堝鎵归『搴忋�佷换鍔$姸鎬佹満銆佹祦杞〃/鐭╅樀銆佹柊澧炲緟纭 11鈥�12锛夈�乣requirement\鍐崇瓥琛╛寰呯‘璁ら」.md`锛堝叏灞�鍙e緞銆丄XU1 琛屽凡鏍稿銆丆-8 宸茶В鍐炽�佹柊澧� C-12鈥揅-15锛夈��
 - 褰撴椂寰呭姙鐨勫悗缁鐞嗭細C-12/C-13/C-14/C-15 鍧囧凡瑙e喅锛汚XU3/AXU4 宸蹭簬 2026-09-16 鏍稿瀹屾垚锛汚 缁勪笅涓�姝ヤ负 AXU5锛孊 缁勫伐绋嬬姸鎬佹満 10 椤逛粛寰呯瓟銆�
 
 **2026-09-14 鐢ㄦ埛绛斿锛堝凡鍥炲~鍏ㄩ儴鏂囨。锛�**锛�
-- 鈶� 鑹囬暱鐜妭涓嶇撼鍏ョ涓�鐗堬紙纭锛夛紱鈶� 鎶ヤ环澶勭悊绉诲嚭涓绘祦绋嬶紝鈥滄姤浠风鐞嗏�濊彍鍗曚笌妯″潡鏁翠綋涓嶅仛锛圕-14 宸茶В鍐筹級锛涒憿 JCC 鍔╃悊瀹℃壒涓哄繀缁忕幆鑺傦紙纭锛夛紱鈶� C-12 绂荤嚎/鏁版嵁鍖呯浉鍏冲姛鑳�**鏁翠綋鍙栨秷锛屽搴旇彍鍗曢」涓�骞跺垹闄�**锛堟暟鎹寘瀵煎叆銆佸畬宸ヤ俊鎭鍏ャ�佸鍑哄緟瀹屽伐鏁版嵁鍖呫�佹姤浠峰鍑�/瀵煎叆锛夛紱鈶� AXU2 鏁版嵁鍖呭鍏ワ細鍙仛缃戠粶鐗堛�佹棤瀵煎叆鍔熻兘锛�**鏃犻渶鏍稿**锛堥〉闈笌鑿滃崟椤瑰垹闄わ級銆�
+- 鈶� T闀跨幆鑺備笉绾冲叆绗竴鐗堬紙纭锛夛紱鈶� 鎶ヤ环澶勭悊绉诲嚭涓绘祦绋嬶紝鈥滄姤浠风鐞嗏�濊彍鍗曚笌妯″潡鏁翠綋涓嶅仛锛圕-14 宸茶В鍐筹級锛涒憿 JCC 鍔╃悊瀹℃壒涓哄繀缁忕幆鑺傦紙纭锛夛紱鈶� C-12 绂荤嚎/鏁版嵁鍖呯浉鍏冲姛鑳�**鏁翠綋鍙栨秷锛屽搴旇彍鍗曢」涓�骞跺垹闄�**锛堟暟鎹寘瀵煎叆銆佸畬宸ヤ俊鎭鍏ャ�佸鍑哄緟瀹屽伐鏁版嵁鍖呫�佹姤浠峰鍑�/瀵煎叆锛夛紱鈶� AXU2 鏁版嵁鍖呭鍏ワ細鍙仛缃戠粶鐗堛�佹棤瀵煎叆鍔熻兘锛�**鏃犻渶鏍稿**锛堥〉闈笌鑿滃崟椤瑰垹闄わ級銆�
 - 宸茶В鍐筹細**C-13 娲惧崟鍙e緞**锛�2026-09-16 纭锛夆�斺�擷CB鍔╃悊瀹℃牳浠嶈礋璐e垎閰嶆壙淇崟浣嶃�佹媶鍗曘�佺敓鎴愬伐绋嬪崟鍙凤紱鍚庣画娲惧崟椤垫寜鏁呴殰閫夋嫨銆佹寜鎵夸慨鍗曚綅鐢熸垚娲惧伐鍗曪紝涓嶅啀閲嶆柊鍒嗛厤鎵夸慨鍗曚綅鎴栦慨鏀瑰巶瀹躲�傚墿浣欙細B 缁勫伐绋嬬姸鎬佹満 10 椤癸紱A 缁勪笅涓�姝� AXU5 CMB棰嗗瀹℃壒銆傦紙C-15 鏈宸蹭簬 2026-09-14 绛斿锛氱粺涓�鍐欌�淭鍛橀獙鏀垛�濓級
 - 鍚庣画杩涘睍锛欰 缁勫凡浜� 2026-09-16 瀹屾垚 AXU3/AXU4锛屼笅涓�姝ヤ负 AXU5 CMB棰嗗瀹℃壒锛圓XU0/AXU1/AXU15 宸插畬鎴愶紝AXU2 鍏嶆牳瀵癸級銆�
 
@@ -469,7 +469,7 @@
 
 褰撳墠绾冲叆妯″潡锛歑CB鍔╃悊娲惧崟锛圓XU10锛夆啋 瀹屽伐鎯呭喌澶勭悊锛圓XU14锛夆啋 瀹屽伐淇℃伅涓婃姤锛圓XU15锛夆啋 瀹屽伐楠屾敹锛堥獙鏀剁敵璇� 鈫� 璐ㄦ鍛橀獙鏀� 鈫� 璐ㄦ璐熻矗浜洪獙鏀� 鈫� 瀹屽伐楠屾敹鎵撳嵃杈撳嚭 鈫� 淇悗璐ㄩ噺璇勭骇锛夆啋 绯荤粺绠$悊 鈫� 缁熻鎶ヨ〃銆傚師涓存姠淇伐绋嬬鐞嗕腑鐨� CMB棰嗗宸查槄锛圓XU9锛夈�佸凡娲惧伐绋嬶紙AXU11锛夈�佸凡娲惧伐绋嬪崟锛圓XU12锛夈�佸伐绋嬪崟鎵撳嵃杈撳嚭锛圓XU13锛変互鍙婃妧鏈枃浠剁鐞嗭紙AXU16锛夋殏涓嶅鐞嗭紱鐩戞祴淇℃伅銆佹煡璇€�佸熀纭�淇℃伅鍜屾妧鏈枃浠剁鐞嗕笉鍐嶄富鍔ㄥ睍寮�锛岄櫎闈炵敤鎴峰悗缁ˉ鍏呴渶姹傘��
 
-鍘熷闇�姹傛祦绋嬶紙瀛樻。锛屽凡涓嶉噰鐢級锛氭晠闅滀笂鎶ワ紙鏁版嵁鍖呭鍏ワ級鈫� 鑹囬暱锛堟壒閲�/鍗曢」锛夆啋 澶ч槦鍔╃悊锛圶CB锛夊鎵� 鈫� CMB棰嗗瀹℃壒 鈫� JCC鍔╃悊瀹℃壒 鈫� JCC澶勯暱瀹℃壒 鈫� 鈥︹�� 鈫� 瀹屽伐淇℃伅涓婃姤 鈫� 楠屾敹锛涘惈鍗曟満鐗�/绂荤嚎鏁版嵁鍖呬笌鎶ヤ环瀵煎嚭瀵煎叆鐜妭銆�
+鍘熷闇�姹傛祦绋嬶紙瀛樻。锛屽凡涓嶉噰鐢級锛氭晠闅滀笂鎶ワ紙鏁版嵁鍖呭鍏ワ級鈫� T闀匡紙鎵归噺/鍗曢」锛夆啋 澶ч槦鍔╃悊锛圶CB锛夊鎵� 鈫� CMB棰嗗瀹℃壒 鈫� JCC鍔╃悊瀹℃壒 鈫� JCC澶勯暱瀹℃壒 鈫� 鈥︹�� 鈫� 瀹屽伐淇℃伅涓婃姤 鈫� 楠屾敹锛涘惈鍗曟満鐗�/绂荤嚎鏁版嵁鍖呬笌鎶ヤ环瀵煎嚭瀵煎叆鐜妭銆�
 
 鍏抽敭瑙勫垯锛堟潵鑷埅鍥惧娉級锛�
 - 娲惧崟瑙勫垯锛�2026-09-20 纭锛夛細JCC澶勯暱瀹℃壒瀹屾垚鍚庤繘鍏CB鍔╃悊娲惧崟椤甸潰锛屾樉绀衡�滄湭娲惧崟鈥濈殑鏁呴殰涓�瑙堬紱鍕鹃�変竴涓垨澶氫釜鏁呴殰鍚庣偣鍑烩�滀竴閿淳鍗曗�濓紝绯荤粺鎸夋瘡涓晠闅滅殑鎵夸慨鍗曚綅鐢熸垚娲惧伐鍗曘�備竴涓晠闅滄湁澶氫釜鎵夸慨鍗曚綅鏃讹紝姣忎釜鎵夸慨鍗曚綅鍚勭敓鎴愪竴寮犳淳宸ュ崟銆�
@@ -517,7 +517,7 @@
 6. 銆愬凡瑙e喅 2026-09-20銆戔�滈敭娲惧崟鈥濈‘璁ゅ簲涓衡�滀竴閿淳鍗曗�濓紱淇敼銆佺敓鎴愪复淇伐绋嬪崟銆佸垹闄や簹澶嶏紙鐤戜负娓呴櫎閲嶅锛夌瓑鍘熸埅鍥句笟鍔℃寜閽殏涓嶉渶瑕併��
 7. 鏃犳埅鍥炬ā鍧楅〉闈㈣璁★細鐩戞祴淇℃伅銆佹煡璇紙宸ョ▼鍗�/宸ョ▼椤圭洰/鏁呴殰淇℃伅/閿�椤癸級銆佹姤浠风鐞嗭紙瀵煎嚭寰呮姤浠�/淇埞鍔炲垵瀹�/JCC瀹℃牳锛夈�佸熀纭�淇℃伅锛圝C淇℃伅/鐢ㄦ埛鏁版嵁鑼冨洿锛夛紱绯荤粺绠$悊鑿滃崟宸茬‘璁ゅ寘鍚壙淇崟浣嶇鐞嗐�佷骇鍝佺粨鏋勬爲绠$悊銆佽澶囧熀纭�搴撶鐞嗭紱瀹屽伐楠屾敹5鑿滃崟宸茬‘璁わ紝瀛楁涓庨〉闈㈢粏鑺備粛寰呰璁°��
 8. 銆愬凡瑙e喅銆戦椤电粺璁″崱鐗囧畬鏁存竻鍗曪紙2026-09-10锛夛細瀹炰负鈥滄寜X鍙风粺璁¤〃鈥濊〃澶� 11 鍒楋紙X鍙�/褰撳墠鐘舵��/鍋滈潬娉婁綅/TD/宸ョ▼鎬绘暟/閿�椤规�绘暟/閲嶅ぇ鏁呴殰/鏈畬宸�/瀹屽伐寰呰瘯楠�/宸插畬宸�/寰呭鐞嗭級锛岄潪鏁e垪鍗$墖锛涒�淛C淇℃伅鈥�=鑸拌埞淇℃伅锛�2026-08-26锛夈�乀D=鑹囬槦锛�2026-09-10锛夊潎宸茬‘璁ゃ��
-9. 銆愬凡瑙e喅 2026-09-11銆戣墖闀跨幆鑺備笉绾冲叆绗竴鐗堬紙绠�鍖栨祦绋嬩粠 T鍛樻晠闅滀笂鎶ョ洿鎺ヨ繘鍏� XCB鍔╃悊瀹℃牳锛夈��
+9. 銆愬凡瑙e喅 2026-09-11銆慣闀跨幆鑺備笉绾冲叆绗竴鐗堬紙绠�鍖栨祦绋嬩粠 T鍛樻晠闅滀笂鎶ョ洿鎺ヨ繘鍏� XCB鍔╃悊瀹℃牳锛夈��
 10. 瀛楀吀鏁版嵁锛氭晠闅滃奖鍝嶏紙绀轰緥鈥滀緵姘斺�濓級銆佹晠闅滄�ц川锛堢ず渚嬧�滈噸澶р�濓級宸茬‘璁ゆ寜鍙厤缃瓧鍏稿瓧娈靛鐞嗭紝鍏朵粬鍙栧�煎緟琛ワ紱X鍙封�滃綋鍓嶇姸鎬佲�濆叾浠栧彇鍊煎緟琛ャ��
 11. 鏈鍕樿锛圤CR锛夛細鐧戒慨=鑷慨銆乀鍛橀槦楠屾敹=T鍛橀獙鏀讹紙2026-09-14 纭缁熶竴鍐欌�淭鍛橀獙鏀垛�濓級锛汬 涓烘晱鎰熻瘝浠gО锛岄潪瀵嗘潗鏂欑粺涓�鍐欌�滄槸鍚︽秹H鈥濓紙2026-09-16锛夈��
 12. 銆愬緟鍔灺锋彁閱掔敤鎴枫�戝櫒鏉愮郴缁熷鎺ユ枃妗o細鐢ㄦ埛宸叉湁鏂囨。锛岄渶瑕佹椂鍚戠敤鎴风储鍙栵紙鍣ㄦ潗鐩綍琛ㄧ粨鏋勩�佸櫒鏉怚D銆佹帴鍙f柟寮忕瓑锛夈��
@@ -593,7 +593,7 @@
 ## 11. 鏀跺熬蹇収锛�2026-09-14 鍛ㄤ竴鏅氾紝鐢ㄦ埛鍏虫満鍓嶏級
 
 - **鏈細璇濓紙2026-09-11 鏅� / 2026-09-14锛夌粨璁哄凡鍏ㄩ儴钀界洏**锛歚requirement\鐣岄潰杩樺師.html`銆乣requirement\鍓�44椤甸渶姹傛⒊鐞�.md`銆乣requirement\宸ョ▼鐘舵�佹満璁捐.md`銆乣requirement\鍐崇瓥琛╛寰呯‘璁ら」.md`銆乣requirement\鏁忔劅璇嶅瓧鍏�.md` + 鏈枃浠讹紱鏃犱复鏃舵枃浠舵畫鐣欙紙宸叉牳楠岋級锛屾棤杩涜涓殑鏈繚瀛樺伐浣溿��
-- **鏈宸茶В鍐崇殑寰呯‘璁ら」**锛欳-8锛堣墖闀跨幆鑺備笉绾冲叆绗竴鐗堬級銆丆-12锛堢绾�/鏁版嵁鍖呭姛鑳芥暣浣撳彇娑� + 瀵瑰簲鑿滃崟椤瑰垹闄わ級銆丆-14锛堟姤浠风鐞嗚彍鍗曚笌妯″潡鏁翠綋涓嶅仛锛夈�丆-15锛堟湳璇粺涓�鍐欌�淭鍛橀獙鏀垛�濓級锛汚XU2 鏁版嵁鍖呭鍏ュ厤鏍稿锛堥〉闈㈠彇娑堬級銆�
+- **鏈宸茶В鍐崇殑寰呯‘璁ら」**锛欳-8锛圱闀跨幆鑺備笉绾冲叆绗竴鐗堬級銆丆-12锛堢绾�/鏁版嵁鍖呭姛鑳芥暣浣撳彇娑� + 瀵瑰簲鑿滃崟椤瑰垹闄わ級銆丆-14锛堟姤浠风鐞嗚彍鍗曚笌妯″潡鏁翠綋涓嶅仛锛夈�丆-15锛堟湳璇粺涓�鍐欌�淭鍛橀獙鏀垛�濓級锛汚XU2 鏁版嵁鍖呭鍏ュ厤鏍稿锛堥〉闈㈠彇娑堬級銆�
 - **浠嶆湭绛斿锛堜笅娆″紑宸ヤ粠杩欓噷鎺ワ級**锛�
   1. C-13 娲惧崟鍙e緞宸蹭簬 2026-09-16 瑙e喅锛歑CB鍔╃悊瀹℃牳璐熻矗鍒嗛厤鎵夸慨鍗曚綅銆佹媶鍗曘�佺敓鎴愬伐绋嬪崟鍙凤紱娲惧崟椤垫寜鏁呴殰閫夋嫨銆佹寜鎵夸慨鍗曚綅鐢熸垚娲惧伐鍗曘��
   2. AXU3/AXU4 宸蹭簬 2026-09-16 瀹屾垚锛汚 缁勪笅涓�姝ヤ负 AXU5 CMB棰嗗瀹℃壒銆�
@@ -830,7 +830,7 @@
 ## 27. 鏀跺熬蹇収锛�2026-10-07 鍛ㄤ笁锛孋MB 瀹℃壒鍚� JCC 鍔╃悊鍒楄〃鏈埛鏂帮級
 
 **闂涓庢牴鍥�**
-- `rk_CMB棰嗗瀹℃壒` 閫氳繃鍚庤繘鍏� `rk_JCC鍔╃悊瀹℃壒`锛岄〉闈㈠彲鑳戒笉鏄剧ず宸茶繘鍏� `CMB_APPROVED` 鐘舵�佺殑鏁版嵁銆�
+- `rk_绗簩瀹℃壒浜哄鎵筦 閫氳繃鍚庤繘鍏� `rk_绗笁瀹℃壒浜哄鎵筦锛岄〉闈㈠彲鑳戒笉鏄剧ず宸茶繘鍏� `CMB_APPROVED` 鐘舵�佺殑鏁版嵁銆�
 - CMB銆丣CC 鍔╃悊銆丣CC 澶勯暱涓変釜瀹℃壒璺敱鍏辩敤 `SimpleApproval.vue`锛屽苟鍏辩敤鍚屼竴涓� keep-alive 缂撳瓨鍚� `SimpleApproval`銆傝矾鐢卞垏鎹㈡椂缁勪欢瀹炰緥琚鐢紝浣嗗師浠g爜鍙湪 `created()` 鏌ヨ涓�娆★紝鏈湪 `stage` 鍙樺寲鏃堕噸鏂板姞杞姐��
 
 **淇鍐呭**
@@ -1040,3 +1040,288 @@
 - 寮�鍙戠幆澧冩晱鎰熻瘝閰嶇疆浼氭妸 `rk_T` 鏄剧ず涓衡�滄鈥濓紝鍥犳椤甸潰鎴浘涓彲鑳芥樉绀衡�滆礋璐f鈥濓紱浠g爜鍜屽悗绔瓧娈典粛鏄� `rk_T`銆�
 - 涓嶈鎻愪氦 `.codex-run`銆丱ffice 閿佹枃浠舵垨 `emergency-repair-web/public/鏂板缓 鏂囨湰鏂囨。.txt`銆�
 - 鏈疆浠g爜銆侀渶姹傛枃浠跺拰浜ゆ帴璁板綍涓�骞舵彁浜ゅ苟鎺ㄩ�佸叕鍙� Git 浠撳簱 `main` 鍒嗘敮銆�
+
+## 27. 鏀跺熬蹇収锛�2026-10-09 鍛ㄤ簲锛岄渶姹� 32 宸ョ▼鍗曟墦鍗颁慨姝d笌楠岃瘉锛�
+
+**鏈疆淇鍐呭**
+- 宸ョ▼鍗� Word 妯℃澘鐨勬渶鍙冲垪鐙珛涓� `UnitNo` 鏍忥紝鍒楀 `400` twips锛涜鍒楄法鑱斿ご銆佽〃澶村拰鏁版嵁琛岀旱鍚戝悎骞讹紝鍗曞厓鏍兼枃瀛楁柟鍚戜负 `btLr`锛堜粠涓嬪埌涓婄珫鎺掞級銆�
+- 绉婚櫎鑱斿ご涓師鏉ョ殑鈥滅{unitNo}鑱斺�濓紝鏀逛负鍙充晶 `UnitNo锛歿unitNo}`锛屽洓鑱斾緷娆℃浛鎹负 `1/2/3/4`銆�
+- 鑱斿ご琛ュ洖 `ZB鍨嬪彿锛歿zbModel}`銆傛覆鏌撴椂鎸夋晠闅� `xNo` 瀹氫綅浜у搧缁撴瀯鏍� `side/SIDE` 鑺傜偣锛屽彇鍏剁埗鑺傜偣鍚嶇О浣滀负 `zbModel`銆�
+- 澧炲姞鍥涜仈鍚嶇О閰嶇疆锛氭瘡鑱斿彲鍦ㄥ悗鍙颁笟鍔¢厤缃腑鍗曠嫭璁剧疆鍚嶇О锛學ord 椤堕儴鎸� `绗�1鑱�    涓婃姤浜篳 鐨勬牸寮忚緭鍑恒��
+- 鏂板缓鐙珛涓氬姟閰嶇疆鏂囦欢 `emergency-repair-server/config/business-config.yml`锛屾妸 `fault-code.prefix`銆佸伐绋嬪崟妯℃澘璺緞鍜屽洓鑱斿悕绉伴泦涓淮鎶わ紝閬垮厤姣忔鏀逛笟鍔″弬鏁伴兘鍔ㄤ富 `application.yml`銆�
+- 榛樿妯℃澘鐗堟湰鍗囩骇涓� `engineering-order-template-v3`锛涘惎鍔ㄦ椂鑻ユā鏉跨己灏戠増鏈爣璁般�乣{zbModel}` 鎴� `{unitDisplay}`锛屼細鑷姩鐢熸垚鏂扮増妯℃澘銆傝繍钀ヤ汉鍛樹粛鍙湪 `emergency-repair-server/config/engineering-order-template.docx` 涓婄敤 Word 璋冩暣鐗堝紡銆�
+- 涓� `application.yml` 閫氳繃 `spring.config.import` 寮曞叆鐙珛涓氬姟閰嶇疆锛涢厤缃枃浠剁己澶辨椂涓嶅奖鍝嶇▼搴忓惎鍔ㄣ��
+
+**涓昏鏂囦欢**
+- `emergency-repair-server/config/business-config.yml`
+- `emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java`
+- `emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java`
+- `emergency-repair-server/src/main/resources/application.yml`
+- `emergency-repair-server/config/engineering-order-template.docx`
+- `emergency-repair-web/src/views/fault/EngineeringPrint.vue`
+
+**楠岃瘉缁撴灉**
+- 灏嗗洓鑱斿悕绉颁复鏃舵敼涓� `1=涓婃姤浜篳銆乣2=瀹℃牳浜篳 鍚庨噸鍚紝鐢熸垚 Word 瀹為檯杈撳嚭 `绗�1鑱�    涓婃姤浜� | 绗�2鑱�    瀹℃牳浜� | 绗�3鑱� | 绗�4鑱擿锛岀‘璁ゅ閮ㄤ笟鍔¢厤缃枃浠跺凡琚鍙栵紱鎭㈠ `2` 涓虹┖鍚庡啀娆¢獙璇佽緭鍑轰负 `绗�1鑱�    涓婃姤浜� | 绗�2鑱� | 绗�3鑱� | 绗�4鑱擿銆�
+- 浣跨敤 `admin / admin123` 鐧诲綍鎴愬姛锛屾煡璇㈠埌 6 鏉℃晠闅滃苟璋冪敤 `POST /api/faults/engineering-print`锛屽疄闄呬笅杞界敓鎴� Word銆�
+- 鐢熸垚鏂囨。瑙f瀽缁撴灉锛�4 涓〃鏍硷紙鍥涜仈锛夈��3 涓垎椤电锛涙瘡鑱� 8 琛岋紝鍏朵腑 6 琛屾暟鎹��
+- 鍥涜仈鍙充晶 `UnitNo` 鍒嗗埆涓� `1/2/3/4`锛涙渶鍙冲崟鍏冩牸鍏峰 `vMerge`銆乣textDirection=btLr`銆乣tcW=400`銆�
+- 鏂囨。涓� `{zbModel}`銆乣{unitNo}`銆乣{unitName}`銆乣{unitDisplay}` 娈嬬暀鏁伴噺鍧囦负 0銆�
+- 褰撳墠浜у搧鏍� `201` 鐨� `side` 鑺傜偣鐖惰妭鐐逛负 `鍨嬪彿s44444`锛涚敓鎴愯仈澶村疄闄呬负 `X鍙凤細201銆�ZB鍨嬪彿锛氬瀷鍙穝44444銆�涓撲笟锛氬姩鍔涗笓涓氣�銆�
+- 鍥涜仈椤堕儴鏍囬瀹為檯涓� `绗�1鑱�    涓婃姤浜篳銆乣绗�2鑱擿銆乣绗�3鑱擿銆乣绗�4鑱擿銆�
+- 鏈疆鍚庣閲嶆柊鎵撳寘骞跺惎鍔ㄦ垚鍔燂紱鍓嶇鏋勫缓姝ゅ墠宸查�氳繃锛屼粎鏈夋棦鏈� webpack 鍖呬綋绉憡璀︺��
+
+**褰撳墠杩愯鐘舵�侊紙2026-10-09锛�**
+- 鍓嶇 `http://localhost:8082`锛孭ID `144352`锛涘悗绔� `http://127.0.0.1:8091`锛孭ID `218764`銆�
+- DM8 `127.0.0.1:5236`锛涚櫥褰� `admin / admin123`锛涗笟鍔� schema `LX`銆�
+- 鍚庣鏃ュ織锛歚.codex-run/backend-20261009-business-config-v3.out.log`銆乣.codex-run/backend-20261009-business-config-v3.err.log`銆�
+- 鏈疆閲嶆柊鎵撳寘骞堕噸鍚悗锛岀鐩樻ā鏉垮凡纭鍗囩骇涓� `engineering-order-template-v3`锛涚嫭绔嬩笟鍔¢厤缃凡鐢熸晥銆�
+
+**鎺ユ墜娉ㄦ剰**
+- 涓嶈鎻愪氦 `.codex-run`銆乣engineering-order-verify.docx`銆丱ffice 閿佹枃浠� `config/~$gineering-order-template.docx` 鎴� `emergency-repair-web/public/鏂板缓 鏂囨湰鏂囨。.txt`銆�
+- `engineering-order-template.docx` 宸叉槸 v3锛涘 WPS 姝e湪鍗犵敤璇ユ枃浠讹紝鍚庣鍚姩鏃朵笉浼氳鐩栵紝鎵撳嵃鏈嶅姟浼氫娇鐢ㄥ唴瀛樹腑鐨� v3 榛樿妯℃澘锛屽叧闂� WPS 鍚庨噸鍚嵆鍙仮澶嶇鐩樻ā鏉胯嚜鍔ㄥ崌绾с��
+- 鑻ュ悗缁慨鏀硅仈澶存垨琛ㄦ牸鍒楋紝妯℃澘鐗堟湰鏍囪浠嶉渶淇濈暀锛涢渶瑕佸己鍒跺崌绾фā鏉挎椂鍐嶆彁鍗� `TEMPLATE_VERSION`銆�
+
+## 29. 淇蹇収锛�2026-10-09 鍛ㄤ簲锛屽伐绋嬪崟鍥涜仈鍚嶇О浣嶇疆淇锛�
+
+**鐢ㄦ埛婢勬竻**
+- 鍥涜仈鍚嶇О涓嶈兘鏀惧湪 Word 椤堕儴浣滀负鏍囬锛屽繀椤诲啓鍦ㄥ師鏉� `UnitNo` 鐨勬渶鍙冲垪绔栨帓浣嶇疆锛屽苟鐢ㄥ畠鏇挎崲鍘熸潵鐨� `UnitNo` 鏂囨湰銆�
+
+**鏈疆淇**
+- 榛樿妯℃澘鐗堟湰鐢� `engineering-order-template-v3` 鍗囩骇涓� `engineering-order-template-v4`銆�
+- 鏈�鍙冲垪鍗曞厓鏍煎崰浣嶇鐢� `UnitNo锛歿unitNo}` 鏀逛负 `{unitDisplay}`锛岀户缁繚鐣� `vMerge`銆乣btLr` 绔栨帓鍜� `400` twips 鍒楀銆�
+- 绉婚櫎琛ㄦ牸涓婃柟鐨� `{unitDisplay}` 鏍囬娈佃惤锛涜緭鍑哄悗鍥涜仈鍙充晶渚濇鏄剧ず `绗�1鑱�    涓婃姤浜篳銆乣绗�2鑱擿銆乣绗�3鑱擿銆乣绗�4鑱擿銆�
+- `config/business-config.yml` 鐨� `unit-names` 閰嶇疆鏂瑰紡涓嶅彉锛屽悗缁彧闇�璋冩暣鍚勮仈鍚嶇О銆�
+
+**楠岃瘉缁撴灉**
+- 鍚庣 `mvn -B -DskipTests compile` 涓� `mvn -B -DskipTests package` 閫氳繃銆�
+- 閲嶅惎鍚庣鐩樻ā鏉垮凡鍗囩骇涓� `engineering-order-template-v4`锛屾ā鏉挎鏂囦腑宸叉棤 `UnitNo`锛宍{unitDisplay}` 浣嶄簬鏈�鍙崇珫鎺掑崟鍏冩牸銆�
+- 瀹為檯璋冪敤 `POST /api/faults/engineering-print` 鐢熸垚 Word 骞惰В鏋愶細4 涓〃鏍笺��3 涓垎椤电锛宍UnitNo` 娈嬬暀 `0`锛宍绗�1鑱�    涓婃姤浜篳 浣嶄簬鏈�鍙崇珫鎺掑崟鍏冩牸锛屽叾浠栦笁鑱斿垎鍒负 `绗�2鑱擿銆乣绗�3鑱擿銆乣绗�4鑱擿銆�
+- 楠岃瘉鏂囦欢锛歚.codex-run/engineering-order-unit-display-right.docx`銆�
+
+**褰撳墠杩愯鐘舵�侊紙2026-10-09锛�**
+- 鍓嶇 `http://localhost:8082`锛孭ID `144352`锛涘悗绔� `http://127.0.0.1:8091`锛孭ID `216492`銆�
+- 鍚庣鏃ュ織锛歚.codex-run/backend-20261009-unit-display-right.out.log`銆乣.codex-run/backend-20261009-unit-display-right.err.log`銆�
+
+## 30. 淇蹇収锛�2026-10-09 鍛ㄤ簲锛屽伐绋嬪崟妯℃澘鍘熶綅濉厖骞朵繚鐣� Word 鏍峰紡锛�
+
+**鐢ㄦ埛婢勬竻**
+- 宸ョ▼鍗曟墦鍗颁笉搴旈噸鏂版瀯閫犱竴浠界┖鐧� Word锛岃�屽簲鎵撳紑鐜版湁 Word 妯℃澘锛屾寜妯℃澘涓殑 `{}` 鏍囪瘑濉啓鏁版嵁锛屽苟淇濈暀妯℃澘鍘熸湁鏍峰紡銆�
+
+**鏈疆淇**
+- `EngineeringOrderPrintService` 鏀逛负鍏堣鍙� `config/engineering-order-template.docx`锛屽湪妯℃澘鏂囨。瀵硅薄涓婂鍒舵鏂囧苟濉厖鏍囪锛屼笉鍐嶉�氳繃 `new XWPFDocument()` 鍒涘缓绌虹櫧鐩爣鏂囨。銆�
+- 淇濈暀妯℃澘鍖呭唴鐨� `styles.xml`銆侀〉鐪夈�侀〉鑴氥�佷富棰樸�佸浘鐗囩瓑璧勬簮锛涙鏂囨钀藉拰琛ㄦ牸鐨勬钀芥牱寮忋�佸瓧浣撱�佸瓧鍙枫�佸姞绮椼�佽竟妗嗐�佸垪瀹界瓑闅忔ā鏉垮壇鏈繚鐣欍��
+- 鏍囪鏇挎崲鍚屾椂鏀寔 `{field}` 鍜� `{{ field }}`锛屽苟鏀寔鏍囪璺ㄥ涓� Word run 鐨勬儏鍐碉紱鏇挎崲鍊间紭鍏堝啓鍏ユ爣璁拌捣濮� run锛屼互淇濈暀璇ュ鍘熸湁瀛椾綋鏍煎紡銆�
+- `ensureTemplate()` 浠呭湪妯℃澘鏂囦欢涓嶅瓨鍦ㄦ椂鐢熸垚榛樿妯℃澘锛涘凡鏈夋ā鏉夸笉鍐嶅洜鐗堟湰鏍囪鎴栧崰浣嶇妫�鏌ヨ鑷姩瑕嗙洊锛屼究浜庤繍钀ヤ汉鍛樼洿鎺ョ敤 Word 缁存姢鐗堝紡銆�
+- `openTemplate()` 濮嬬粓浼樺厛浣跨敤纾佺洏妯℃澘锛岀鐩樻ā鏉跨己澶辨椂鎵嶅洖閫�鍒板唴瀛橀粯璁ゆā鏉裤��
+
+**涓昏鏂囦欢**
+- `emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java`
+- `emergency-repair-server/config/engineering-order-template.docx`
+
+**楠岃瘉缁撴灉**
+- 鍚庣 `mvn -B -DskipTests package` 閫氳繃銆�
+- 浣跨敤褰撳墠榛樿妯℃澘璋冪敤 `POST /api/faults/engineering-print`锛岀敓鎴� `.codex-run/engineering-order-template-style-smoke.docx`锛氬崰浣嶇娈嬬暀 `0`銆佸洓鑱斿ご `4` 涓�佸垎椤电 `3` 涓��
+- 鍙︾敤甯� `styles.xml`銆�3 缁勯〉鐪夐〉鑴氥�乣theme1.xml`銆乣fontTable.xml` 鐨� `涓翠慨宸ョ▼瀹屽伐楠屾敹鍗�.docx` 浣滀负妯℃澘锛屽湪鐙珛 `8092` 绔彛鍚姩鍚庣鐢熸垚 `.codex-run/engineering-order-styled-template-output.docx`銆�
+- 杈撳嚭涓� `styles.xml`銆侀〉鐪夐〉鑴氥�佷富棰樸�佸瓧浣撹〃鍧囧瓨鍦紱缁� XML 瑙勮寖鍖栧姣旓紝鏍峰紡銆侀〉鐪夈�侀〉鑴氬唴瀹逛笌婧愭ā鏉夸竴鑷淬�傝緭鍑轰腑娌℃湁 `{}` 鎴� `{{}}` 娈嬬暀锛屽洓鑱斿拰 3 涓垎椤电姝e父銆�
+- 鏍峰紡楠岃瘉鏈熼棿涓存椂 `8092` 杩涚▼宸插仠姝紝涓诲悗绔拰鍓嶇淇濇寔杩愯銆�
+
+**褰撳墠杩愯鐘舵�侊紙2026-10-09锛�**
+- 鍓嶇 `http://localhost:8082`锛孭ID `144352`锛涘悗绔� `http://127.0.0.1:8091`锛孭ID `224748`銆�
+- 鍚庣鏃ュ織锛歚.codex-run/backend-20261009-template-style.out.log`銆乣.codex-run/backend-20261009-template-style.err.log`銆�
+- 鐧诲綍 `admin / admin123`锛涙暟鎹簱 `SYSDBA/SYSDBA`锛屼笟鍔� schema `LX`銆�
+
+**鎺ユ墜娉ㄦ剰**
+- 宸叉湁 `engineering-order-template.docx` 浼氭寜鍘熸牱浣跨敤锛屼笉鍐嶈嚜鍔ㄥ崌绾ц鐩栵紱濡傞渶閲嶅缓榛樿妯℃澘锛屽厛澶囦唤骞跺垹闄よ鏂囦欢锛屾垨鍦ㄤ唬鐮佷腑鏄惧紡鐢熸垚銆�
+- 涓嶈鎻愪氦 `.codex-run`銆丱ffice 閿佹枃浠舵垨 `emergency-repair-web/public/鏂板缓 鏂囨湰鏂囨。.txt`銆�
+
+## 31. 淇蹇収锛�2026-10-09 鍛ㄤ簲锛屽伐绋嬪崟闈炴槑缁嗚〃閲嶅鏈淇锛�
+
+**闂鐜拌薄**
+- 宸ョ▼鍗曟ā鏉垮寘鍚笂鏂� X 鍙�/鍨嬪彿淇℃伅琛ㄣ�佷腑闂存晠闅滄槑缁嗚〃鍜屼笅鏂圭瀛楄〃銆傛墦鍗板悗涓婃柟淇℃伅琛ㄧ殑鏈�鍚庝竴琛岃澶嶅埗鎴愬鏉★紝涓嬫柟绛惧瓧鏃ユ湡琛屼篃琚噸澶嶅鍔犮��
+
+**鍘熷洜涓庝慨姝�**
+- 鍘熼�昏緫鍙妯℃澘涓嚭鐜颁换鎰� `{}` 鍗犱綅绗︼紝灏辨妸姣忎竴寮犺〃鐨勬渶鍚庝竴琛岄兘鎸夎褰曟暟灞曞紑銆�
+- 鐜版敼涓烘寜琛ㄦ牸鍐呭璇嗗埆锛氬彧鏈夊寘鍚� `faultCode`銆乣equipmentName`銆乣repairRequirement` 绛夋槑缁嗗瓧娈电殑琛ㄦ牸鎵嶅睍寮�鏈�鍚庝竴琛岋紱淇℃伅琛ㄥ拰绛惧瓧琛ㄥ彧鏇挎崲瀛楁锛屼笉澶嶅埗琛屻��
+
+**楠岃瘉缁撴灉**
+- 鍚庣 `mvn -B -DskipTests package` 閫氳繃銆�
+- 浣跨敤褰撳墠瀹為檯妯℃澘鍜� 6 鏉℃晠闅滄暟鎹敓鎴� `.codex-run/engineering-order-template-row-fix.docx`銆�
+- 绗� 1 寮犱俊鎭〃鍥哄畾 `2` 琛岋紝绗� 2 寮犳槑缁嗚〃涓� `7` 琛岋紙琛ㄥご 1 琛� + 鏁版嵁 6 琛岋級锛岀 3 寮犵瀛楄〃鍥哄畾 `3` 琛岋紱鍥涜仈鍧囦竴鑷淬��
+- 杈撳嚭鍗犱綅绗︽畫鐣� `0`锛屽垎椤电 `3` 涓��
+
+**褰撳墠杩愯鐘舵�侊紙2026-10-09锛�**
+- 鍓嶇 `http://localhost:8082`锛孭ID `144352`锛涘悗绔� `http://127.0.0.1:8091`锛孭ID `225740`銆�
+- 鍚庣鏃ュ織锛歚.codex-run/backend-20261009-template-table-rows.out.log`銆乣.codex-run/backend-20261009-template-table-rows.err.log`銆�
+
+## 32. 淇蹇収锛�2026-10-09 鍛ㄤ簲锛屽伐绋嬪崟绛惧瓧琛ㄥ浐瀹氫竴椤靛簳閮級
+
+**闂鐜拌薄**
+- 妯℃澘鎶婄瀛楄〃绉诲埌椤甸潰鏈�涓嬫柟锛屼腑闂寸敤 11 涓┖娈佃惤鎾戦珮銆�6 鏉℃槑缁嗘墦鍗板悗锛岀瀛楄〃琚尋鍒颁笅涓�椤碉紱鍥涜仈瀹為檯杈撳嚭涓� 7 椤碉紝涓棿杩樻湁绌虹櫧椤点��
+
+**鍘熷洜涓庝慨姝�**
+- 绌烘钀芥拺楂樹細闅忔槑缁嗚鏁板彉鍖栵紝6 琛屾槑缁嗘椂蹇呯劧婧㈠嚭锛涘師鏉ュ洓鑱旈棿浣跨敤鐙珛鍒嗛〉娈佃惤锛屽湪涓婁竴椤垫帓婊℃椂涔熷彲鑳藉厛钀藉埌涓嬩竴椤碉紝鍐嶄骇鐢熺┖鐧介〉銆�
+- 娓叉煋鏃跺垹闄ゆ槑缁嗚〃涓庡簳閮ㄧ瀛楄〃涔嬮棿鐨勭函绌烘钀斤紝浠ュ強绛惧瓧琛ㄥ悗鐨勭函绌烘钀姐��
+- 灏嗘ā鏉挎渶鍚庝竴寮犺〃璁句负 `vertAnchor=margin`銆乣tblpYSpec=bottom`锛岀洿鎺ュ浐瀹氬埌鏂囨湰鍖哄煙搴曢儴锛屼笉鍐嶄緷璧栫┖娈佃惤鎾戦珮銆�
+- 鍥涜仈鍒嗛〉鏀逛负鍦ㄤ笅涓�鑱旈涓鏂囧厓绱犱笂璁剧疆 `w:pageBreakBefore`锛屼笉鍐嶉澶栨彃鍏ョ嫭绔嬪垎椤垫钀斤紝閬垮厤绌虹櫧椤点��
+
+**楠岃瘉缁撴灉**
+- 鍚庣 `mvn -B -DskipTests package` 閫氳繃銆�
+- 6 鏉¤褰曠敓鎴� `.codex-run/engineering-order-template-one-page.docx`锛學ord 瀹為檯缁熻 `4` 椤碉紝鍥涜仈鍒嗗埆鍦ㄧ `1/2/3/4` 椤点��
+- 姣忚仈琛ㄦ牸鍥哄畾涓猴細淇℃伅琛� `2` 琛屻�佹槑缁嗚〃 `7` 琛屻�佺瀛楄〃 `3` 琛岋紱绛惧瓧琛ㄧ旱鍚戜綅缃害 `427.9pt`锛屼綅浜庢枃鏈尯鍩熷簳閮紝鏈笌鏄庣粏琛ㄩ噸鍙犮��
+- 2 鏉¤褰曠敓鎴� `.codex-run/engineering-order-template-two-records.docx`锛屼粛涓� `4` 椤碉紝绛惧瓧琛ㄥ悓鏍峰浐瀹氬湪搴曢儴銆�
+- 杈撳嚭涓� `pageBreakBefore` 涓� `3` 涓紝鍗犱綅绗︽畫鐣� `0`銆�
+
+**褰撳墠杩愯鐘舵�侊紙2026-10-09锛�**
+- 鍓嶇 `http://localhost:8082`锛孭ID `144352`锛涘悗绔� `http://127.0.0.1:8091`锛孭ID `223880`銆�
+- 鍚庣鏃ュ織锛歚.codex-run/backend-20261009-template-one-page.out.log`銆乣.codex-run/backend-20261009-template-one-page.err.log`銆�
+
+## 33. 淇蹇収锛�2026-10-10 鏄熸湡鍏紝宸ョ▼鍗曟墦鍗板崟鏉¤褰曠姝㈣法椤碉級
+
+**鐢ㄦ埛婢勬竻**
+- 宸ョ▼鍗曟墦鍗颁笉闄愬埗姣忛〉璁板綍鏉℃暟锛屽彲浠ュ椤佃緭鍑猴紱鍏抽敭鏄�**涓�鏉℃晠闅滆褰曚笉鑳借法椤�**锛屼笖璁板綍楂樺害闅忔枃鏈灏戝彉鍖栵紝蹇呴』鍔ㄦ�佽绠楋紝涓嶈兘鍐欐琛岄珮鎴栨潯鏁般��
+- 绗� 1 涓〃鏍硷紙X 鍙�/鍨嬪彿淇℃伅锛変笌绗� 3 涓〃鏍硷紙绛惧瓧琛級鍒嗗埆鍥哄畾鍦ㄦ瘡椤甸《閮ㄥ拰搴曢儴锛岀敱绋嬪簭鍔ㄦ�佺敓鎴愶紝妯℃澘涓嶉渶瑕佸啀鏀逛綅缃��
+
+**鏈疆淇**
+- `EngineeringOrderPrintService.configureDetailTablePagination` 缁х画瀵规瘡涓�琛岃缃� `w:cantSplit`锛堣涓嶅彲鎷嗭級銆�
+- 鏂板 `configureCellParagraphPagination(row)`锛氬琛屽唴鎵�鏈夊崟鍏冩牸娈佃惤璁剧疆 `w:keepLines`锛堟钀戒笉鍙媶鍒颁袱椤碉級銆�
+- 涓よ�呴厤鍚堝悗锛岃楂樹粛鐢� Word 鎸夊疄闄呮枃鏈姩鎬佹帓鐗堬紝娌℃湁鍥哄畾琛岄珮銆佸浐瀹氭瘡椤垫潯鏁版垨瀛楃鏁伴槇鍊笺��
+
+**涓昏鏂囦欢**
+- `emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java`锛堢 488-514 琛岄檮杩戯級
+
+**楠岃瘉缁撴灉**
+- 鍚庣 `mvn -B -DskipTests clean package` 閫氳繃锛圝DK `C:\Program Files\Java\jdk1.8.0_172`锛孧aven `D:\solidDir\apache-maven-3.6.1`锛夈��
+- 瀹為檯璋冪敤 `POST /api/faults/engineering-print`锛�6 鏉℃晠闅滐級鐢熸垚 `.codex-run/engineering-order-dynamic-pagination-2.docx`锛歚cantSplit` 28 涓�乣keepLines` 224 涓�乣tblHeader` 4 涓�佸洓鑱斿垎椤� `pageBreakBefore` 3 涓�佸崰浣嶇娈嬬暀 0銆�
+- 姝e父鏁版嵁缁� Word 瀵煎嚭 `.codex-run/engineering-order-normal-fresh.pdf` 涓� 4 椤碉紝涓�鑱斾竴椤点��
+- 闀挎枃鏈竟鐣屾祴璇曪細鎶婄 1 鏉℃槑缁嗙殑鍗曞厓鏍兼枃鏈斁澶у埌绾� 900 瀛楃锛坄.codex-run/engineering-order-long-row-fresh2.docx`锛夛紝Word 閲嶆柊鍒嗛〉鍚庝负 5 椤碉紱`.codex-run/engineering-order-long-row-fresh2.pdf` 涓璁板綍鐨勫叏閮ㄦ枃鏈彧鍑虹幇鍦ㄧ 1 椤碉紝鍏朵綑 5 鏉¤褰曚笌绛惧瓧琛ㄩ『寤跺埌绗� 2 椤碉紝璁板綍鏈法椤点��
+- 娓叉煋鎴浘锛歚.codex-run/word-export-long-row-fresh/page-1.png` 鑷� `page-5.png`锛涢�愰〉瀛楃缁熻鑴氭湰 `.codex-run/count_marker_chars.py`銆�
+- 娉ㄦ剰锛氱敤 .NET `ZipArchive` 鏀瑰啓 docx 浼氳 Word 鎶モ�滄枃浠跺彲鑳藉凡缁忔崯鍧忊�濓紝蹇呴』鐢� Python `zipfile` 閲嶅啓娴嬭瘯鏂囨。銆�
+
+**褰撳墠杩愯鐘舵�侊紙2026-10-10锛�**
+- 鍚庣 `http://127.0.0.1:8091`锛孭ID `180416`锛屾爣鍑� fat jar 鍚姩锛歚java -cp target\emergency-repair-server-1.0.0-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher`銆�
+- 鍚庣鏃ュ織锛歚.codex-run/backend-20261010-dynamic-pagination-8.out.log`銆乣.codex-run/backend-20261010-dynamic-pagination-8.err.log`銆�
+- 鍓嶇 `http://localhost:8082`锛岃繘绋� PID `232852`锛屾棩蹇楋細`.codex-run/frontend-20261010-dynamic-pagination.out.log`銆乣.codex-run/frontend-20261010-dynamic-pagination.err.log`銆�
+- 鐧诲綍 `admin / admin123`锛屾墦鍗版帴鍙f祴璇曚娇鐢� `Authorization: Bearer demo-admin-token`銆�
+
+**鎺ユ墜娉ㄦ剰**
+- 鏋佺鎯呭喌涓嬭嫢鍗曟潯璁板綍鐨勫疄闄呮帓鐗堥珮搴﹁秴杩囦竴鏁撮〉锛屼换浣� Word 琛岀骇绾︽潫閮芥棤娉曡瀹冨畬鏁存斁鍦ㄤ竴椤碉紱褰撳墠涓氬姟鏁版嵁涓嶅瓨鍦ㄨ鎯呭喌銆�
+- 涓嶈鎻愪氦 `.codex-run`銆丱ffice 閿佹枃浠舵垨 `emergency-repair-web/public/鏂板缓 鏂囨湰鏂囨。.txt`銆�
+
+## 34. 淇蹇収锛�2026-10-10 鏄熸湡鍏紝宸ョ▼鍗曟寜鏄庣粏鍒嗙粍杩炵画鎵撳嵃鍥涜仈锛�
+
+**鐢ㄦ埛婢勬竻**
+- 鍚屼竴涓槑缁嗘暟鎹殑 4 鑱斿繀椤昏繛缁墦鍗帮紝鍐嶆墦鍗颁笅涓�涓槑缁嗘暟鎹殑 4 鑱旓紝涓嶈兘鍏堟墦鍗版墍鏈夋槑缁嗙殑绗� 1 鑱斿啀鎵撳嵃绗� 2 鑱斻��
+
+**鏈疆淇**
+- `EngineeringOrderPrintService.renderUnits` 璋冩暣涓哄灞傞亶鍘嗘槑缁嗚褰曘�佸唴灞傞亶鍘� 1-4 鑱斻��
+- 姣忓紶鏄庣粏琛ㄥ彧娓叉煋褰撳墠涓�鏉¤褰曪紝骞跺湪涓嬩竴寮犺仈琛ㄥ墠璁剧疆鍒嗛〉锛涗繚鐣欏崟鏉¤褰� `cantSplit + keepLines`锛岄伩鍏嶈褰曡法椤点��
+- 涓昏鏂囦欢锛歚emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java`銆�
+
+**楠岃瘉缁撴灉**
+- 浣跨敤 6 鏉℃晠闅滄暟鎹皟鐢� `POST /api/faults/engineering-print`锛岀敓鎴� `.codex-run/engineering-order-per-record-units.docx` 鍜屽搴� PDF銆�
+- DOCX 椤跺眰鍏冪礌椤哄簭纭锛氭槑缁� 1 鐨勭1/2/3/4鑱旇繛缁紝闅忓悗鏄槑缁� 2銆�3銆�4銆�5銆�6 鐨勫悇鑷洓鑱斻��
+- 鍏� 48 寮犺仈琛ㄣ��24 椤碉紱姣忚仈鏍囩鍚� 6 娆★紝鍗犱綅绗︽畫鐣� 0锛宍pageBreakBefore` 23 涓��
+- PDF 椤靛垎缁勭‘璁わ細绗� 1-4 椤典负鏄庣粏 1锛岀 5-8 椤典负鏄庣粏 2锛屼緷娆″埌绗� 21-24 椤典负鏄庣粏 6銆�
+- 鍚庣褰撳墠杩愯 PID `243948`锛岀洃鍚� `http://127.0.0.1:8091`锛涘墠绔� PID `232852`锛岀洃鍚� `http://localhost:8082`銆�
+- 鍚庣鏃ュ織锛歚.codex-run/backend-20261010-per-record-units.out.log`銆乣.codex-run/backend-20261010-per-record-units.err.log`銆�
+
+## 35. 淇蹇収锛�2026-10-10 鏄熸湡鍏紝宸ョ▼鍗曟墦鍗板垎椤甸棶棰樺畾浣嶏級
+
+**鐢ㄦ埛鍙嶉**
+- 鐢ㄦ埛鍙嶉鈥滅粨鏋滀笉瀵癸紝姣忛〉鏄庣粏鏁版嵁閮藉彧鏈変竴鏉♀�濄��
+
+**瀹氫綅缁撹**
+- 鏈�鍒濇��鐤戠敤鎴锋墦寮�鐨勬槸 `2026-10-10 12:45:28` 鐢熸垚鐨勬棫鐗堟枃浠讹紝浣嗗悗缁‘璁よ缁撹涓嶆垚绔嬶細鐪熸鐨勬牴鍥犳槸褰撳墠浠g爜鍦ㄥ睍寮�鏄庣粏琛ㄦ椂锛屽彧鏇挎崲浜嗘暟鎹锛屾ā鏉夸腑浣滀负鑱斿ご鐨勭 1 琛岀敤浜嗙┖鍊兼浛鎹紝瀵艰嚧鍙充晶绔栨帓鍗曞厓鏍肩殑鑱斿彿/鑱斿悕琚啓鎴愮┖瀛楃涓层�傝瑙佺 36 鑺傘��
+- 褰撳墠浠g爜宸叉敼涓哄崟寮犳槑缁嗚〃鍐呮寜鈥滆褰� 1 鐨勭 1-4 鑱斻�佽褰� 2 鐨勭 1-4 鑱斺�︹�︹�濋『搴忔覆鏌擄紝骞朵繚鐣� `cantSplit + keepLines`锛涗笉鍐嶆妸鍚屼竴鏁呴殰鐨勫洓鑱旀暣浣� `keepNext` 缁戝畾锛岄伩鍏嶉暱璁板綍鎶婃暣缁勬帹鍒颁笅涓�椤点��
+
+**褰撳墠浠g爜楠岃瘉**
+- 鐢ㄥ悓涓�鎵� 6 鏉℃晠闅滈噸鏂拌皟鐢� `POST /api/faults/engineering-print`锛岀敓鎴� `.codex-run/engineering-order-current.docx`銆�
+- 褰撳墠 DOCX 鍙湁 `1` 寮犳槑缁嗚〃锛岀煭璁板綍鍙湪鍚岄〉鎺掑鏉★紝鍥涜仈椤哄簭杩炵画锛涜仈鍙�/鑱斿悕鐨勬渶缁堥獙璇佽绗� 36 鑺傘��
+- 褰撳墠鏂囦欢鍙﹀瓨楠岃瘉鍓湰锛歚C:\Users\Lenovo\Downloads\宸ョ▼鍗�-褰撳墠鍚庡彴楠岃瘉-20261010-135834.docx`銆�
+
+**杈圭晫璇存槑**
+- 鍏ㄩ噺 22 鏉℃暟鎹祴璇曟椂锛岄儴鍒嗘祴璇曡褰曠殑鈥滃伐绋嬪悕绉板強鎹熷潖鎯呭喌鈥濊秴杩� 300 涓瓧绗︼紝鍗曡仈楂樺害宸叉帴杩戞垨瓒呰繃涓�椤靛彲鐢ㄦ槑缁嗗尯鍩燂紱杩欑鎯呭喌涓� WPS 鏃犳硶鎶婂洓鑱旀垨鍚岄〉澶氭潯璁板綍寮鸿鍘嬪湪椤靛唴锛屽嵆浣垮幓鎺� `keepNext` 涔熶粛浼氫竴鑱斾竴椤点��
+- 杩欎笉鏄綋鍓嶅垎缁勪唬鐮侀�犳垚鐨勶紱褰撳墠浠g爜宸叉秷闄や簡鏃х増鈥滀竴鑱斾竴寮犻《灞傝〃鏍尖�濈殑纭垎椤点��
+
+**杩愯鐘舵�侊紙2026-10-10 13:58锛�**
+- 鍓嶇 `http://localhost:8082`锛孭ID `232852`锛岀鍙e凡鐩戝惉銆�
+- 鍚庣 `http://127.0.0.1:8091`锛孭ID `247224`锛屽惎鍔ㄦ椂闂� `2026-10-10 13:46:23`锛岀鍙e凡鐩戝惉銆�
+
+## 36. 淇蹇収锛�2026-10-10 鏄熸湡鍏紝宸ョ▼鍗曡仈鍙�/鑱斿悕鏈樉绀轰慨澶嶏級
+
+**鐢ㄦ埛鍙嶉**
+- 鐢ㄦ埛鍙嶉鈥滆仈鍙峰拰鑱斿悕椤垫病鏄剧ず鈥濓紝鍗� Word 宸ョ▼鍗曟渶鍙充晶绔栨帓鍗曞厓鏍奸噷鐨勨�滅N鑱� 涓婃姤浜衡�濅负绌恒��
+
+**鏍瑰洜**
+- 妯℃澘鏄庣粏琛ㄧ 1 琛屾槸鑱斿ご琛岋紙鍚渶鍙充晶 `{unitDisplay}` 绔栨帓鍗曞厓鏍硷級锛屾渶鍚庝竴琛屾槸鏁版嵁琛屻��
+- 鏃ч�昏緫鍙睍寮�骞跺啓鍏ユ暟鎹锛岃仈澶磋鐢ㄧ┖鍊兼浛鎹紝瀵艰嚧鑱斿彿/鑱斿悕琚啓鎴愮┖瀛楃涓诧紝杈撳嚭閲屽彧鍓╂暟鎹�佹病鏈夎仈澶存爣璇嗐��
+
+**浠g爜淇敼**
+- `EngineeringOrderPrintService.renderUnits` 鏀逛负璋冪敤 `renderDetailTable(detailTable, records, unitNames)`銆�
+- 鏂板 `renderDetailTable`锛氭寜鈥滄晠闅�1鐨勭1鑷�4鑱旓紝鏁呴殰2鐨勭1鑷�4鑱斺�︹�︹�濋�愮粍澶嶅埗妯℃澘鑱斿ご琛屽拰鏁版嵁琛岋紝骞跺姣忚仈姝g‘鍐欏叆 `unitNo`銆乣unitName`銆乣unitDisplay`銆�
+- `unitDisplay` 鏍煎紡涓� `绗琋鑱擿锛岄厤缃簡鍚嶇О鏃惰拷鍔犲洓涓┖鏍煎拰鍚嶇О锛屼緥濡� `绗�1鑱�    涓婃姤浜篳銆�
+- `configureDetailTablePagination` 淇濈暀姣忚 `cantSplit` 鍜屾钀� `keepLines`锛屾樉寮忔竻闄ゆ棫 `keepNext`锛屽彧璁╄仈澶磋涓庤嚜宸辩殑鏁版嵁琛屼繚鎸� `keepNext`锛涗笉鍐嶆妸鍚屼竴鏁呴殰鐨勫洓鑱旂粦鎴愭暣浣撱��
+
+**閰嶇疆**
+- 鑱斿悕浠嶅彇鑷� `emergency-repair-server/config/business-config.yml` 鐨� `app.engineering-print.unit-names`銆�
+- 褰撳墠閰嶇疆锛歚1: 涓婃姤浜篳锛宍2/3/4` 涓虹┖锛屽洜姝ょ 2/3/4 鑱斿彧鏄剧ず鈥滅2鑱�/绗�3鑱�/绗�4鑱斺�濄��
+
+**楠岃瘉缁撴灉**
+- 鍚庣 `mvn -B -DskipTests package` 閫氳繃锛圝DK `C:\Program Files\Java\jdk1.8.0_172`锛孧aven `D:\solidDir\apache-maven-3.6.1`锛夈��
+- 鏂板悗绔� PID `248156`锛岀洃鍚� `http://127.0.0.1:8091`锛涘墠绔� PID `232852`锛岀洃鍚� `http://localhost:8082`銆�
+- 浣跨敤鍓� 17 鏉℃晠闅滆皟鐢� `POST /api/faults/engineering-print`锛孒eader 涓� `Authorization: Bearer demo-admin-token`锛岀敓鎴� `.codex-run/engineering-order-labels-verified.docx` 涓� `.pdf`銆�
+- XML 妫�鏌ワ細1 寮犳槑缁嗚〃銆�136 琛岋紝鍗� 17 鏉℃晠闅� 脳 4 鑱� 脳 2 琛屻��
+- `绗�1鑱�    涓婃姤浜篳銆乣绗�2鑱擿銆乣绗�3鑱擿銆乣绗�4鑱擿 鍚勫嚭鐜� 17 娆★紱`{unitNo}`銆乣{unitName}`銆乣{unitDisplay}` 娈嬬暀鍧囦负 0銆�
+- PDF 鍏� 29 椤碉紝宸叉覆鏌撳埌 `.codex-run/engineering-order-labels-pages/`锛涚 1銆�4 椤靛彲瑙佹渶鍙充晶绔栨帓鑱斿彿锛岀 1 椤垫湁鏄庣粏锛屼笉鍐嶅彧鏈夐《閮ㄥ拰绛惧瓧琛ㄣ��
+
+**杩愯鐘舵�侊紙2026-10-10 14:21锛�**
+- 鍓嶇 `http://localhost:8082`锛孭ID `232852`銆�
+- 鍚庣 `http://127.0.0.1:8091`锛孭ID `248156`锛屽惎鍔ㄦ椂闂� `2026-10-10 14:20:00`銆�
+- 鍚庣鏃ュ織锛歚.codex-run/backend-20261010-unit-labels.out.log`銆乣.codex-run/backend-20261010-unit-labels.err.log`銆�
+- 楠岃瘉鏂囦欢锛歚.codex-run/engineering-order-labels-verified.docx`銆乣.codex-run/engineering-order-labels-verified.pdf`銆�
+
+**娉ㄦ剰浜嬮」**
+- 涓嶈閲嶆柊鎶婂悓涓�鏁呴殰鐨勫洓鑱旂敤 `keepNext` 缁戞垚鏁村潡锛屽惁鍒欓暱璁板綍浼氭妸鏁寸粍鎺ㄥ埌涓嬩竴椤点��
+- 鑻ョ敤鎴峰啀鐪嬫棫鏂囦欢浼氳浠ヤ负鏈慨澶嶏紝闇�閲嶆柊鍦ㄩ〉闈㈣Е鍙戜竴娆℃墦鍗帮紱鏃� `.docx` 涓嶄細琚悗鍙板洖鍐欍��
+- 涓嶈鎻愪氦 `.codex-run`銆丱ffice 閿佹枃浠舵垨 `emergency-repair-web/public/鏂板缓 鏂囨湰鏂囨。.txt`銆�
+
+## 37. 淇蹇収锛�2026-10-10 鏄熸湡鍏紝鍘熸墦鍗板伐绋嬪崟鎸夐�昏緫椤佃緭鍑哄洓鑱旓級
+
+**鐢ㄦ埛婢勬竻**
+- 鍘熲�滄墦鍗板伐绋嬪崟鈥濈殑閲嶅鍗曚綅鏄�昏緫椤碉紝涓嶆槸鍗曟潯璁板綍锛氬厛鎸夎褰曞姩鎬佸垎椤碉紝鍐嶆妸姣忎釜閫昏緫椤电殑鏁版嵁鍒嗗埆杈撳嚭涓虹 1銆�2銆�3銆�4 鑱斻��
+- 鈥滄墦鍗板伐绋嬪崟2鈥濆凡缁忕ǔ瀹氾紝鏈疆涓嶆敼瀹冪殑 `renderLegacyGroupedUnits()` 鍜屾渶鍚庝竴鍒楃旱鍚戝悎骞堕�昏緫銆�
+
+**浠g爜淇敼**
+- `EngineeringOrderPrintService.renderPagedUnits()` 淇濈暀椤电骇鍥涜仈杈撳嚭锛氭瘡涓�昏緫椤电敓鎴� 1-4 鑱旓紝姣忚仈鍗曠嫭鍒嗛〉銆�
+- `paginateRecords()` 鐨勫彲鐢ㄩ珮搴︾郴鏁扮敱 `0.75` 璋冩暣涓� `0.50`锛岄槻姝㈤暱鏂囨湰鏄庣粏鎶婂悓涓�鑱旇〃鎸ゅ埌缁〉銆�
+- 鏄庣粏琛岀户缁繚鐣� `cantSplit + keepLines`锛屼竴鏉¤褰曚笉鑳借法椤碉紱妯℃澘椤电湁鍜岀瀛楅〉鑴氱户缁�氳繃 Word header/footer 鍦ㄦ瘡椤甸噸澶嶃��
+- 涓昏鏂囦欢锛歚emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java`銆�
+
+**楠岃瘉缁撴灉**
+- 鍚庣 `mvn -q -DskipTests package` 閫氳繃銆�
+- 浣跨敤 `.codex-run/engineering-print-all-request.json` 鐨� 22 鏉℃晠闅滆皟鐢� `POST /api/faults/engineering-print`锛岀敓鎴� `.codex-run/engineering-order-page-repeat-v3.docx`銆�
+- XML 缁撴瀯锛�28 寮犺仈琛ㄣ��28 涓仈鍙锋爣璁帮紱Word 瀹為檯 28 椤碉紝绛変簬 7 涓�昏緫椤典箻 4 鑱旓紝娌℃湁鑱旇〃缁〉銆�
+- Word 妫�鏌ョ‘璁ゆ瘡椤甸兘鏈夐〉鐪夊拰绛惧瓧椤佃剼锛涚 1 鑱斿埌绗� 4 鑱旀寜閫昏緫椤佃繛缁緭鍑恒��
+
+**杩愯鐘舵�侊紙2026-10-10 16:48锛�**
+- 鍓嶇 `http://localhost:8082`锛孭ID `232852`锛岀鍙e凡鐩戝惉銆�
+- 鍚庣 `http://127.0.0.1:8091`锛孭ID `261356`锛岀鍙e凡鐩戝惉锛涚敱褰撳墠 Codex 缁堢鍓嶅彴鎵樼銆�
+- 鐧诲綍璐﹀彿 `admin / admin123`銆�
+
+## 38. 淇蹇収锛�2026-10-10 鏄熸湡鍏紝宸ョ▼鍗曟墦鍗伴〉闈㈠幓鎺夎〃鏍煎唴閮ㄦ粴鍔ㄦ潯锛�
+
+**鐢ㄦ埛鍙嶉**
+- 璋冩暣甯冨眬鍘绘帀鏈�鍙充晶宸ヤ綔鍖烘粴鍔ㄦ潯鍚庯紝琛ㄦ牸鍐呴儴鍙堝嚭鐜颁簡婊氬姩鏉★紱鐢ㄦ埛涓嶅笇鏈涜〃鏍兼湰韬樉绀烘粴鍔ㄦ潯銆�
+
+**鏈疆淇**
+- `EngineeringPrint.vue` 淇濇寔椤甸潰绾靛悜 flex 甯冨眬锛屽伐浣滃尯涓嶅啀浜х敓澶栨粴鍔ㄦ潯銆�
+- 瀵� Element UI 鐨� `.el-table__body-wrapper` 鍜� `.el-table__fixed-body-wrapper` 鍚屾椂闅愯棌婊氬姩鏉★紝婊氬姩鑳藉姏淇濈暀锛岄伩鍏� 20 鏉℃暟鎹椂搴曢儴鍐呭鏃犳硶璁块棶銆�
+- 涓昏鏂囦欢锛歚emergency-repair-web/src/views/fault/EngineeringPrint.vue`銆�
+
+**楠岃瘉缁撴灉**
+- CDP 妗岄潰瑙嗗彛 `1440x900`锛氬伐浣滃尯 `clientHeight=792`銆乣scrollHeight=792`锛屽婊氬姩鏉′笉瀛樺湪銆�
+- `.el-table__body-wrapper` 浠嶅彲婊氬姩锛岃绠楁牱寮� `scrollbar-width: none`锛屾粴鍔ㄦ潯涓嶅彲瑙併��
+- 椤甸潰鎴浘锛歚.codex-run/engineering-print-layout-current.png`銆�
+
+**褰撳墠杩愯鐘舵�侊紙2026-10-10锛�**
+- 鍓嶇 `http://localhost:8082`锛孭ID `232852`锛涘悗绔� `http://127.0.0.1:8091`锛孭ID `261356`銆�
diff --git a/emergency-repair-server/pom.xml b/emergency-repair-server/pom.xml
index 1144a48..751e684 100644
--- a/emergency-repair-server/pom.xml
+++ b/emergency-repair-server/pom.xml
@@ -29,6 +29,22 @@
             <artifactId>spring-boot-starter-validation</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>4.1.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml-schemas</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>ooxml-schemas</artifactId>
+            <version>1.4</version>
+        </dependency>
+        <dependency>
             <groupId>org.springframework.security</groupId>
             <artifactId>spring-security-crypto</artifactId>
         </dependency>
diff --git a/emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java b/emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java
index 0cc1f2c..9615625 100644
--- a/emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java
+++ b/emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java
@@ -89,6 +89,7 @@
         seedMenusAndRoles();
         ensureSystemMenus();
         ensureCompletionMenus();
+        ensureEngineeringPrintMenu();
         normalizeSensitiveTerms();
         normalizeEquipmentTerms();
         seedButtonMenus();
@@ -116,14 +117,15 @@
             createMenu("dashboard", null, "棣栭〉闂ㄦ埛", "/dashboard", "el-icon-s-home", 1, 10);
             Long fault = createMenu("fault", null, "鏁呴殰绠$悊", null, "el-icon-warning-outline", 0, 20);
             createMenu("fault-report", fault, "鏁呴殰涓婃姤", "/fault/report", null, 1, 10);
-            createMenu("xcb-review", fault, "rk_XCB鍔╃悊瀹℃牳", "/fault/review", null, 1, 20);
-            createMenu("cmb-approval", fault, "rk_CMB棰嗗瀹℃壒", "/fault/approval/cmb", null, 1, 30);
-            createMenu("jcc-assistant", fault, "rk_JCC鍔╃悊瀹℃壒", "/fault/approval/jcc-assistant", null, 1, 40);
-            createMenu("jcc-director", fault, "rk_JCC澶勯暱瀹℃壒", "/fault/approval/jcc-director", null, 1, 50);
-            createMenu("dispatch", fault, "rk_XCB鍔╃悊娲惧崟", "/fault/dispatch", null, 1, 60);
+            createMenu("xcb-review", fault, "rk_绗竴瀹℃壒浜哄鏍�", "/fault/review", null, 1, 20);
+            createMenu("cmb-approval", fault, "rk_绗簩瀹℃壒浜哄鎵�", "/fault/approval/cmb", null, 1, 30);
+            createMenu("jcc-assistant", fault, "rk_绗笁瀹℃壒浜哄鎵�", "/fault/approval/jcc-assistant", null, 1, 40);
+            createMenu("jcc-director", fault, "rk_绗洓瀹℃壒浜哄鎵�", "/fault/approval/jcc-director", null, 1, 50);
+            createMenu("dispatch", fault, "rk_绗竴瀹℃壒浜烘淳鍗�", "/fault/dispatch", null, 1, 60);
             createMenu("completion-list", fault, "瀹屽伐鎯呭喌澶勭悊", "/fault/completion", null, 1, 70);
             createMenu("completion-report", fault, "瀹屽伐淇℃伅涓婃姤", "/completion/report", null, 1, 80);
             createMenu("completion-query", fault, "瀹屽伐淇℃伅鏌ラ槄", "/completion/query", null, 1, 90);
+            createMenu("engineering-print", fault, "宸ョ▼鍗曟墦鍗�", "/fault/engineering-print", null, 1, 100);
 
             Long acceptance = createMenu("acceptance", null, "瀹屽伐楠屾敹", null, "el-icon-circle-check", 0, 30);
             createMenu("accept-application", acceptance, "楠屾敹鐢宠", "/acceptance/application", null, 1, 10);
@@ -281,6 +283,16 @@
         grantAdminMenus("/completion/query");
     }
 
+    private void ensureEngineeringPrintMenu() {
+        SysMenu fault = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
+                .eq(SysMenu::getName, "鏁呴殰绠$悊")
+                .eq(SysMenu::getMenuType, 0)
+                .last("LIMIT 1"));
+        Long pid = fault == null ? 0L : fault.getId();
+        ensureMenu(pid, "宸ョ▼鍗曟墦鍗�", "/fault/engineering-print", 100);
+        grantAdminMenus("/fault/engineering-print");
+    }
+
     private void ensureSystemMenus() {
         SysMenu system = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
                 .eq(SysMenu::getName, "\u7cfb\u7edf\u7ba1\u7406").last("LIMIT 1"));
diff --git a/emergency-repair-server/src/main/java/com/emergencyrepair/business/controller/FaultController.java b/emergency-repair-server/src/main/java/com/emergencyrepair/business/controller/FaultController.java
index c836b87..dc5ced1 100644
--- a/emergency-repair-server/src/main/java/com/emergencyrepair/business/controller/FaultController.java
+++ b/emergency-repair-server/src/main/java/com/emergencyrepair/business/controller/FaultController.java
@@ -5,8 +5,13 @@
 import com.emergencyrepair.business.entity.Fault;
 import com.emergencyrepair.business.entity.WorkOrder;
 import com.emergencyrepair.business.service.BusinessService;
+import com.emergencyrepair.business.service.EngineeringOrderPrintService;
 import com.emergencyrepair.common.Result;
 import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -19,6 +24,9 @@
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.time.LocalDate;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 
 @RestController
@@ -26,6 +34,7 @@
 @RequiredArgsConstructor
 public class FaultController {
     private final BusinessService businessService;
+    private final EngineeringOrderPrintService engineeringOrderPrintService;
 
     @GetMapping("/page")
     public Result<Page<Fault>> page(@RequestParam(defaultValue = "1") long current,
@@ -112,4 +121,41 @@
     public Result<List<WorkOrder>> dispatch(@RequestBody Requests.DispatchRequest request) {
         return Result.ok(businessService.dispatch(request));
     }
+
+    /**
+     * 宸ョ▼鍗曟墦鍗帮細鎶婇�変腑鐨勬晠闅滆褰曞鐢� Word 妯℃澘鐢熸垚鍥涜仈宸ョ▼鍗曘��
+     */
+    @PostMapping("/engineering-print")
+    public ResponseEntity<byte[]> engineeringPrint(@RequestBody Requests.IdList request) {
+        return documentResponse(engineeringOrderPrintService.render(request.getIds()),
+                "宸ョ▼鍗�.docx", "engineering-order.docx");
+    }
+
+    /**
+     * 鎵撳嵃宸ョ▼鍗�2锛氱1鑱斿叏閮ㄦ槑缁嗗悗鎹㈤〉锛屽啀杈撳嚭绗�2鑱旓紝浠ユ绫绘帹銆�
+     */
+    @PostMapping("/engineering-print-v2")
+    public ResponseEntity<byte[]> engineeringPrintV2(@RequestBody Requests.IdList request) {
+        return documentResponse(engineeringOrderPrintService.renderVersion2(request.getIds()),
+                "宸ョ▼鍗�2.docx", "engineering-order-2.docx");
+    }
+
+    private ResponseEntity<byte[]> documentResponse(byte[] document, String displayName, String fallbackName) {
+        String filename = encodeFilename(displayName, fallbackName);
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.parseMediaType(
+                "application/vnd.openxmlformats-officedocument.wordprocessingml.document"));
+        headers.set(HttpHeaders.CONTENT_DISPOSITION,
+                "attachment; filename=\"" + fallbackName + "\"; filename*=UTF-8''" + filename);
+        headers.setContentLength(document.length);
+        return new ResponseEntity<>(document, headers, HttpStatus.OK);
+    }
+
+    private String encodeFilename(String name, String fallbackName) {
+        try {
+            return URLEncoder.encode(name, StandardCharsets.UTF_8.name()).replace("+", "%20");
+        } catch (UnsupportedEncodingException exception) {
+            return fallbackName;
+        }
+    }
 }
diff --git a/emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java b/emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java
index f2c5329..4ba1218 100644
--- a/emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java
+++ b/emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java
@@ -47,7 +47,7 @@
 public class BusinessService {
     private static final DateTimeFormatter FAULT_DATE_FORMAT = DateTimeFormatter.ofPattern("yyMMdd");
     private static final Object FAULT_CODE_LOCK = new Object();
-    @Value("${fault-code.prefix:\u6f5c1\u57fa-}")
+    @Value("${fault-code.prefix:鍥哄畾鍓嶇紑-}")
     private String faultCodePrefix;
     private final FaultMapper faultMapper;
     private final JdbcTemplate jdbcTemplate;
@@ -253,7 +253,7 @@
             throw new BusinessException("璇峰~鍐欓攢椤圭悊鐢�");
         }
         String cancelledBy = StringUtils.hasText(request.getCancelledBy())
-                ? request.getCancelledBy().trim() : "rk_XCB鍔╃悊";
+                ? request.getCancelledBy().trim() : "rk_绗竴瀹℃壒浜�";
         for (Long id : request.getIds()) {
             Fault fault = requireFault(id);
             if (!StatusConstants.Task.REPORTED_XCB.equals(fault.getTaskStatus())
@@ -307,7 +307,7 @@
             Fault fault = requireFault(id);
             if (!StatusConstants.Task.REPORTED_XCB.equals(fault.getTaskStatus())
                     && !StatusConstants.Task.RETURNED.equals(fault.getTaskStatus())) {
-                throw new BusinessException("鏁呴殰 " + fault.getFaultCode() + " 褰撳墠鐘舵�佷笉鍏佽rk_XCB鍔╃悊瀹℃牳");
+                throw new BusinessException("鏁呴殰 " + fault.getFaultCode() + " 褰撳墠鐘舵�佷笉鍏佽rk_绗竴瀹℃壒浜哄鏍�");
             }
             index++;
             fault.setContractorIds(contractorIds);
@@ -334,21 +334,21 @@
             LocalDateTime now = LocalDateTime.now();
             switch (request.getStage()) {
                 case "CMB":
-                    requireStatus(fault, StatusConstants.Task.XCB_REVIEWED, "rk_CMB棰嗗瀹℃壒");
+                    requireStatus(fault, StatusConstants.Task.XCB_REVIEWED, "rk_绗簩瀹℃壒浜哄鎵�");
                     fault.setCmbOpinion(request.getOpinion());
                     fault.setCmbResult(pass ? "閫氳繃" : "涓嶉�氳繃");
                     fault.setCmbTime(now);
                     fault.setTaskStatus(pass ? StatusConstants.Task.CMB_APPROVED : StatusConstants.Task.RETURNED);
                     break;
                 case "JCC_ASSISTANT":
-                    requireStatus(fault, StatusConstants.Task.CMB_APPROVED, "rk_JCC鍔╃悊瀹℃壒");
+                    requireStatus(fault, StatusConstants.Task.CMB_APPROVED, "rk_绗笁瀹℃壒浜哄鎵�");
                     fault.setJccAssistantOpinion(request.getOpinion());
                     fault.setJccAssistantResult(pass ? "閫氳繃" : "涓嶉�氳繃");
                     fault.setJccAssistantTime(now);
                     fault.setTaskStatus(pass ? StatusConstants.Task.JCC_ASSISTANT_APPROVED : StatusConstants.Task.RETURNED);
                     break;
                 case "JCC_DIRECTOR":
-                    requireStatus(fault, StatusConstants.Task.JCC_ASSISTANT_APPROVED, "rk_JCC澶勯暱瀹℃壒");
+                    requireStatus(fault, StatusConstants.Task.JCC_ASSISTANT_APPROVED, "rk_绗洓瀹℃壒浜哄鎵�");
                     fault.setJccDirectorOpinion(request.getOpinion());
                     fault.setJccDirectorResult(pass ? "閫氳繃" : "涓嶉�氳繃");
                     fault.setJccDirectorTime(now);
diff --git a/emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java b/emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java
new file mode 100644
index 0000000..27aea89
--- /dev/null
+++ b/emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java
@@ -0,0 +1,1400 @@
+package com.emergencyrepair.business.service;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.emergencyrepair.business.entity.CompletionInfo;
+import com.emergencyrepair.business.entity.Fault;
+import com.emergencyrepair.business.entity.WorkOrder;
+import com.emergencyrepair.business.mapper.CompletionInfoMapper;
+import com.emergencyrepair.business.mapper.FaultMapper;
+import com.emergencyrepair.business.mapper.WorkOrderMapper;
+import com.emergencyrepair.business.entity.ProductNode;
+import com.emergencyrepair.business.mapper.ProductNodeMapper;
+import com.emergencyrepair.common.BusinessException;
+import lombok.RequiredArgsConstructor;
+import org.apache.poi.ooxml.POIXMLProperties;
+import org.apache.poi.wp.usermodel.HeaderFooterType;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFFooter;
+import org.apache.poi.xwpf.usermodel.XWPFHeader;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+import org.apache.poi.xwpf.usermodel.XWPFTable;
+import org.apache.poi.xwpf.usermodel.XWPFTableCell;
+import org.apache.poi.xwpf.usermodel.XWPFTableRow;
+import org.apache.xmlbeans.XmlObject;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSpacing;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTrPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblBorders;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGrid;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblCellMar;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcMar;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STYAlign;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBorder;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STPageOrientation;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STLineSpacingRule;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTextDirection;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVAnchor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.PostConstruct;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 宸ョ▼鍗曟墦鍗帮紙闇�姹� 32锛夈��
+ *
+ * <p>鎶婇�変腑鐨勬晠闅滆褰曟寜 Word 妯℃澘杈撳嚭鎴� A4 妯悜鐨勫洓鑱斿伐绋嬪崟锛屾槑缁嗗彲鑷姩鍒嗛〉銆�
+ * 妯℃澘閲岀敤 {@code {瀛楁鍚峿} 鍗犱綅锛屾覆鏌撴椂鏇挎崲鎴愮湡瀹炴暟鎹��</p>
+ *
+ * <p>妯℃澘鏂囦欢榛樿鏀惧湪 {@code config/engineering-order-template.docx}锛岄娆″惎鍔ㄨ嫢涓嶅瓨鍦ㄤ細鑷姩鐢熸垚涓�浠斤紝
+ * 杩愮淮/涓氬姟浜哄憳鍙互鐩存帴鐢� Word 鎵撳紑淇敼鏍囬銆佸垪瀹姐�佽仈澶存枃瀛楋紝涓嶉渶瑕佹敼浠g爜銆�</p>
+ *
+ * <p>涓氬姟閰嶇疆鏀惧湪 {@code config/business-config.yml}锛屽彲鎸夎仈鍙风淮鎶ょ 1~4 鑱斿悕绉般��</p>
+ *
+ * <p>妯℃澘绾﹀畾锛堝寘鍚槑缁嗗瓧娈电殑琛ㄦ牸锛夛細</p>
+ * <ul>
+ *   <li>鏄庣粏琛ㄦ渶鍚庝竴琛屾槸鏁版嵁琛屾ā鏉匡紝浼氭寜閫変腑璁板綍鏁板鍒跺睍寮�锛岃褰曡涓嶅厑璁歌法椤垫媶鍒嗭紱</li>
+ *   <li>鑱旂骇瀛楁鍙互鐢� {@code {xNo}}銆亄@code {teamMajor}}銆亄@code {maxFinishDate}}銆亄@code {unitNo}}銆亄@code {unitName}}銆亄@code {unitDisplay}}锛�</li>
+ *   <li>鏁翠唤妯℃澘浼氳澶嶅埗 4 娆¤緭鍑轰负鍥涜仈锛堟瘡鑱斾竴椤碉級锛寋@code {unitNo}} 渚濇涓� 1/2/3/4銆�</li>
+ * </ul>
+ */
+@Service
+@RequiredArgsConstructor
+public class EngineeringOrderPrintService {
+    private static final Logger log = LoggerFactory.getLogger(EngineeringOrderPrintService.class);
+
+    /** 涓�涓� Word 鏂囦欢閲岀殑鑱旀暟銆� */
+    private static final int UNIT_COUNT = 4;
+
+    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+    private static final Pattern PLACEHOLDER = Pattern.compile("\\{([^{}]*)\\}");
+    private static final String TEMPLATE_VERSION = "engineering-order-template-v4";
+    private static final Pattern TEMPLATE_PLACEHOLDER =
+            Pattern.compile("\\{\\{\\s*([^{}]+?)\\s*\\}\\}|\\{\\s*([^{}]+?)\\s*\\}");
+
+    /** 鍙湁鍖呭惈鏄庣粏瀛楁鐨勮〃鏍兼墠鎸夎褰曟暟灞曞紑鏈�鍚庝竴琛屻�� */
+    private static final Set<String> DETAIL_PLACEHOLDERS = new HashSet<>(Arrays.asList(
+            "faultCode", "engineeringNo", "workOrderNo", "equipmentName", "quantity", "equipmentCount",
+            "parentSystem", "engineeringMajor", "faultImpact", "faultNature", "majorFaultDesc",
+            "repairRequirement", "engineeringNameDesc", "isSelfRepair", "isHRelated", "isMonitorItem",
+            "monitorItem", "monitorValue", "requestedFinishDate", "remark", "contractorName",
+            "contractorNames", "startDate", "finishDate", "owner", "reportUnit", "reportDate"
+    ));
+
+
+    private static final BigInteger A4_LANDSCAPE_WIDTH = BigInteger.valueOf(16838);
+    private static final BigInteger A4_LANDSCAPE_HEIGHT = BigInteger.valueOf(11906);
+    private static final BigInteger PAGE_MARGIN = BigInteger.valueOf(567);
+
+    /** 榛樿妯℃澘鐨勮仈澶村崰浣嶆枃瀛椼�� */
+    private static final String DEFAULT_UNIT_HEADER =
+            "X鍙凤細{xNo}銆�ZB鍨嬪彿锛歿zbModel}銆�涓撲笟锛歿teamMajor}銆�鏈�澶ц姹傚畬宸ユ椂闂达細{maxFinishDate}";
+
+    /** 榛樿妯℃澘鐨勫垪瀹氫箟锛氭爣棰� + 瀛楁鍗犱綅绗︺�� */
+    private static final String[][] DEFAULT_COLUMNS = {
+            {"鏁呴殰缂栧彿", "{faultCode}"},
+            {"璁惧鍚嶇О", "{equipmentName}"},
+            {"宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�", "{engineeringNameDesc}"},
+            {"淇悊鎶�鏈姹�", "{repairRequirement}"},
+            {"鎵夸慨鍗曚綅", "{contractorName}"},
+            {"鏁伴噺", "{quantity}"},
+            {"澶囨敞", "{remark}"}
+    };
+
+    /** 榛樿妯℃澘鐨勫垪瀹斤紙twips锛夛紝鍚堣绾︾瓑浜� A4 妯悜鍙敤瀹藉害銆� */
+    private static final int UNIT_COLUMN_WIDTH = 400;
+    private static final int[] TABLE_COLUMN_WIDTHS = {1450, 2750, 3300, 3300, 2000, 800, 1700, UNIT_COLUMN_WIDTH};
+
+    private final FaultMapper faultMapper;
+    private final WorkOrderMapper workOrderMapper;
+    private final CompletionInfoMapper completionInfoMapper;
+    private final ProductNodeMapper productNodeMapper;
+
+    @Value("${app.engineering-print.template-path:${app.engineering-print.template:config/engineering-order-template.docx}}")
+    private String templateLocation;
+
+    @Value("${app.engineering-print.unit-names.1:涓婃姤浜簘")
+    private String unitName1;
+
+    @Value("${app.engineering-print.unit-names.2:}")
+    private String unitName2;
+
+    @Value("${app.engineering-print.unit-names.3:}")
+    private String unitName3;
+
+    @Value("${app.engineering-print.unit-names.4:}")
+    private String unitName4;
+
+    @PostConstruct
+    public void ensureTemplate() {
+        Path path = templatePath();
+        if (path == null) {
+            return;
+        }
+        try {
+            Files.createDirectories(path.getParent());
+            if (Files.exists(path)) {
+                return;
+            }
+            try (XWPFDocument document = buildDefaultTemplate();
+                 ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+                document.write(out);
+                Files.write(path, out.toByteArray());
+            }
+            log.info("宸ョ▼鍗曟墦鍗版ā鏉垮凡鐢熸垚鎴栧崌绾э細{}", path.toAbsolutePath());
+        } catch (Exception exception) {
+            log.warn("鐢熸垚鎴栧崌绾у伐绋嬪崟鎵撳嵃妯℃澘澶辫触锛歿}", exception.getMessage());
+        }
+    }
+
+    private Map<Integer, String> loadUnitNames() {
+        Map<Integer, String> names = new LinkedHashMap<>();
+        names.put(1, text(unitName1).trim());
+        names.put(2, text(unitName2).trim());
+        names.put(3, text(unitName3).trim());
+        names.put(4, text(unitName4).trim());
+        return names;
+    }
+
+    private boolean templateNeedsUpgrade(Path path) {
+        try (InputStream input = Files.newInputStream(path);
+             XWPFDocument document = new XWPFDocument(input)) {
+            POIXMLProperties.CoreProperties properties = document.getProperties().getCoreProperties();
+            return !TEMPLATE_VERSION.equals(properties.getKeywords())
+                    || collectText(document).indexOf("{zbModel}") < 0
+                    || collectText(document).indexOf("{unitDisplay}") < 0;
+        } catch (Exception exception) {
+            log.warn("宸ョ▼鍗曟墦鍗版ā鏉挎棤娉曡鍙栵紝灏嗛噸鏂扮敓鎴愶細{}", exception.getMessage());
+            return true;
+        }
+    }
+
+    /**
+     * 鎸夐�変腑鐨勬晠闅滆褰曟覆鏌撳伐绋嬪崟 Word 鏂囨。銆�
+     *
+     * @param faultIds 鏁呴殰涓婚敭
+     */
+    public byte[] render(List<Long> faultIds) {
+        return renderInternal(faultIds, false);
+    }
+
+    /**
+     * 鎵撳嵃宸ョ▼鍗�2锛氭寜鑱斿垎缁勮緭鍑猴紝绗�1鑱斿叏閮ㄦ槑缁嗗悗鎹㈤〉锛屽啀杈撳嚭绗�2鑱旓紝浠ユ绫绘帹銆�
+     */
+    public byte[] renderVersion2(List<Long> faultIds) {
+        return renderInternal(faultIds, true);
+    }
+
+    private byte[] renderInternal(List<Long> faultIds, boolean legacyGroupedUnits) {
+        List<Long> ids = normalizeIds(faultIds);
+        if (ids.isEmpty()) {
+            throw new BusinessException("璇疯嚦灏戦�夋嫨涓�鏉¤褰曞悗鍐嶆墦鍗�");
+        }
+        List<Map<String, String>> records = loadRecords(ids);
+        if (records.isEmpty()) {
+            throw new BusinessException("閫変腑鐨勮褰曞凡涓嶅瓨鍦紝璇峰埛鏂板悗閲嶈瘯");
+        }
+        Map<Integer, String> unitNames = loadUnitNames();
+        try (InputStream template = openTemplate();
+             XWPFDocument document = new XWPFDocument(template);
+             ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
+            if (legacyGroupedUnits) {
+                renderLegacyGroupedUnits(document, records, unitNames);
+            } else {
+                renderUnits(document, records, unitNames);
+            }
+            document.write(buffer);
+            return buffer.toByteArray();
+        } catch (IOException exception) {
+            throw new BusinessException("鐢熸垚宸ョ▼鍗曞け璐ワ細" + exception.getMessage());
+        }
+    }
+
+    private List<Long> normalizeIds(List<Long> faultIds) {
+        if (faultIds == null) {
+            return new ArrayList<>();
+        }
+        List<Long> ids = new ArrayList<>();
+        for (Long id : faultIds) {
+            if (id != null && !ids.contains(id)) {
+                ids.add(id);
+            }
+        }
+        return ids;
+    }
+
+    private List<Map<String, String>> loadRecords(List<Long> ids) {
+        List<Fault> faults = faultMapper.selectBatchIds(ids);
+        Map<Long, Fault> faultById = new LinkedHashMap<>();
+        for (Fault fault : faults) {
+            faultById.put(fault.getId(), fault);
+        }
+        Map<String, String> zbModelByXNo = loadZbModels(faults);
+
+        List<WorkOrder> workOrders = workOrderMapper.selectList(Wrappers.<WorkOrder>lambdaQuery()
+                .in(WorkOrder::getFaultId, ids));
+        Map<Long, WorkOrder> workOrderByFault = new LinkedHashMap<>();
+        List<Long> workOrderIds = new ArrayList<>();
+        for (WorkOrder workOrder : workOrders) {
+            workOrderByFault.putIfAbsent(workOrder.getFaultId(), workOrder);
+            if (workOrder.getId() != null) {
+                workOrderIds.add(workOrder.getId());
+            }
+        }
+
+        Map<Long, CompletionInfo> completionByWorkOrder = new LinkedHashMap<>();
+        if (!workOrderIds.isEmpty()) {
+            List<CompletionInfo> completions = completionInfoMapper.selectList(Wrappers.<CompletionInfo>lambdaQuery()
+                    .in(CompletionInfo::getWorkOrderId, workOrderIds));
+            for (CompletionInfo completion : completions) {
+                completionByWorkOrder.putIfAbsent(completion.getWorkOrderId(), completion);
+            }
+        }
+
+        LocalDate maxFinishDate = null;
+        for (CompletionInfo completion : completionByWorkOrder.values()) {
+            if (completion.getFinishDate() != null
+                    && (maxFinishDate == null || completion.getFinishDate().isAfter(maxFinishDate))) {
+                maxFinishDate = completion.getFinishDate();
+            }
+        }
+        if (maxFinishDate == null) {
+            for (Long id : ids) {
+                Fault fault = faultById.get(id);
+                if (fault != null && fault.getRequestedFinishDate() != null
+                        && (maxFinishDate == null || fault.getRequestedFinishDate().isAfter(maxFinishDate))) {
+                    maxFinishDate = fault.getRequestedFinishDate();
+                }
+            }
+        }
+
+        List<Map<String, String>> records = new ArrayList<>();
+        for (Long id : ids) {
+            Fault fault = faultById.get(id);
+            if (fault == null) {
+                continue;
+            }
+            WorkOrder workOrder = workOrderByFault.get(id);
+            CompletionInfo completion = workOrder == null ? null : completionByWorkOrder.get(workOrder.getId());
+            String zbModel = zbModelByXNo.get(normalizeXNo(fault.getXNo()));
+            records.add(buildRecord(fault, workOrder, completion, maxFinishDate, zbModel));
+        }
+        return records;
+    }
+
+    private Map<String, String> loadZbModels(List<Fault> faults) {
+        Map<String, String> result = new LinkedHashMap<>();
+        List<String> xNos = new ArrayList<>();
+        for (Fault fault : faults) {
+            String xNo = normalizeXNo(fault.getXNo());
+            if (StringUtils.hasText(xNo) && !xNos.contains(xNo)) {
+                xNos.add(xNo);
+            }
+        }
+        if (xNos.isEmpty()) {
+            return result;
+        }
+
+        List<ProductNode> sideNodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery()
+                .in(ProductNode::getLayer, java.util.Arrays.asList("side", "SIDE"))
+                .in(ProductNode::getName, xNos)
+                .ne(ProductNode::getIsDelete, "1")
+                .orderByAsc(ProductNode::getSortNo)
+                .orderByAsc(ProductNode::getId));
+        Map<String, Long> parentIdsByXNo = new LinkedHashMap<>();
+        for (ProductNode side : sideNodes) {
+            String xNo = normalizeXNo(side.getName());
+            if (StringUtils.hasText(xNo) && side.getParentId() != null && side.getParentId() != 0L) {
+                parentIdsByXNo.putIfAbsent(xNo, side.getParentId());
+            }
+        }
+        if (parentIdsByXNo.isEmpty()) {
+            return result;
+        }
+
+        List<Long> parentIds = new ArrayList<>(new java.util.LinkedHashSet<>(parentIdsByXNo.values()));
+        List<ProductNode> parents = productNodeMapper.selectBatchIds(parentIds);
+        Map<Long, String> parentNameById = new LinkedHashMap<>();
+        for (ProductNode parent : parents) {
+            if (!"1".equals(parent.getIsDelete())) {
+                parentNameById.put(parent.getId(), text(parent.getName()));
+            }
+        }
+        for (Map.Entry<String, Long> entry : parentIdsByXNo.entrySet()) {
+            result.put(entry.getKey(), text(parentNameById.get(entry.getValue())));
+        }
+        return result;
+    }
+
+    private String normalizeXNo(String value) {
+        return StringUtils.hasText(value) ? value.trim() : "";
+    }
+
+    private Map<String, String> buildRecord(Fault fault, WorkOrder workOrder, CompletionInfo completion,
+                                            LocalDate maxFinishDate, String zbModel) {
+        Map<String, String> values = new LinkedHashMap<>();
+        String contractor = firstText(
+                workOrder == null ? null : workOrder.getContractorName(),
+                completion == null ? null : completion.getContractorName(),
+                fault.getContractorNames());
+        String engineeringNo = firstText(
+                completion == null ? null : completion.getEngineeringNo(),
+                fault.getEngineeringNo());
+
+        values.put("faultCode", text(fault.getFaultCode()));
+        values.put("engineeringNo", text(engineeringNo));
+        values.put("workOrderNo", text(workOrder == null ? null : workOrder.getWorkOrderNo()));
+        values.put("equipmentName", text(fault.getEquipmentName()));
+        values.put("quantity", String.valueOf(equipmentQuantity(fault)));
+        values.put("equipmentCount", values.get("quantity"));
+        values.put("parentSystem", text(fault.getParentSystem()));
+        values.put("engineeringMajor", text(fault.getEngineeringMajor()));
+        values.put("teamMajor", text(fault.getTeamMajor()));
+        values.put("xNo", text(fault.getXNo()));
+        values.put("zbModel", text(zbModel));
+        values.put("owner", text(fault.getOwner()));
+        values.put("reportUnit", text(fault.getReportUnit()));
+        values.put("reportDate", date(fault.getReportDate()));
+        values.put("faultImpact", text(fault.getFaultImpact()));
+        values.put("faultNature", text(fault.getFaultNature()));
+        values.put("majorFaultDesc", text(fault.getMajorFaultDesc()));
+        values.put("repairRequirement", text(fault.getRepairRequirement()));
+        values.put("engineeringNameDesc", text(fault.getEngineeringNameDesc()));
+        values.put("isSelfRepair", yesNo(fault.getIsSelfRepair()));
+        values.put("isHRelated", yesNo(fault.getIsHRelated()));
+        values.put("isMonitorItem", yesNo(fault.getIsMonitorItem()));
+        values.put("monitorItem", text(fault.getMonitorItem()));
+        values.put("monitorValue", text(fault.getMonitorValue()));
+        values.put("requestedFinishDate", date(fault.getRequestedFinishDate()));
+        values.put("remark", text(fault.getRemark()));
+        values.put("contractorName", text(contractor));
+        values.put("contractorNames", text(fault.getContractorNames()));
+        values.put("startDate", date(completion == null ? null : completion.getStartDate()));
+        values.put("finishDate", date(completion == null ? null : completion.getFinishDate()));
+        values.put("maxFinishDate", date(maxFinishDate));
+        return values;
+    }
+
+    private int equipmentQuantity(Fault fault) {
+        String equipmentIds = fault.getEquipmentIds();
+        int count = 0;
+        if (StringUtils.hasText(equipmentIds)) {
+            for (String item : equipmentIds.split(",")) {
+                if (StringUtils.hasText(item)) {
+                    count++;
+                }
+            }
+        }
+        if (count == 0 && fault.getEquipmentId() != null) {
+            count = 1;
+        }
+        return count;
+    }
+
+    private void renderUnits(XWPFDocument document,
+                             List<Map<String, String>> records, Map<Integer, String> unitNames) {
+        CTBody body = document.getDocument().getBody();
+        List<XmlObject> templateElements = snapshotBodyElements(document);
+        if (templateElements.isEmpty()) {
+            throw new BusinessException("Engineering order template is empty: " + templateLocation);
+        }
+
+        int detailTableIndex = findDetailTableIndex(templateElements);
+        if (detailTableIndex < 0) {
+            throw new BusinessException("Engineering order template has no detail table: " + templateLocation);
+        }
+        int bottomTableIndex = findBottomTableIndex(templateElements, detailTableIndex);
+        Map<String, String> shared = new LinkedHashMap<>(records.get(0));
+
+        CTSectPr section = body.isSetSectPr() ? (CTSectPr) body.getSectPr().copy() : null;
+        CTBody cleanBody = CTBody.Factory.newInstance();
+        if (section != null) {
+            cleanBody.addNewSectPr().set(section);
+        }
+        body.set(cleanBody);
+
+        // Render each dynamically paginated detail page as four consecutive units.
+
+        renderPagedUnits(body, (CTTbl) templateElements.get(detailTableIndex), records, unitNames, section);
+
+        moveSectionPropertiesToEnd(body);
+        copyTemplatePrefixToHeader(document, templateElements, detailTableIndex, shared, records);
+        if (bottomTableIndex >= 0) {
+            copyTemplateTableToFooter(document, (CTTbl) templateElements.get(bottomTableIndex), shared, records);
+        }
+    }
+
+    private void renderPagedUnits(CTBody body, CTTbl templateTable,
+                                  List<Map<String, String>> records,
+                                  Map<Integer, String> unitNames,
+                                  CTSectPr section) {
+        List<List<Map<String, String>>> pages = paginateRecords(templateTable, records, section);
+        int renderedPage = 0;
+        for (List<Map<String, String>> page : pages) {
+            for (int unit = 1; unit <= UNIT_COUNT; unit++) {
+                String unitName = text(unitNames.get(unit)).trim();
+                Map<String, String> unitValues = new LinkedHashMap<>();
+                unitValues.put("unitNo", String.valueOf(unit));
+                unitValues.put("unitName", unitName);
+                unitValues.put("unitDisplay", "\u7b2c" + unit + "\u8054"
+                        + (StringUtils.hasText(unitName) ? "    " + unitName : ""));
+
+                List<Map<String, String>> unitRecords = new ArrayList<>();
+                for (Map<String, String> record : page) {
+                    Map<String, String> unitRecord = new LinkedHashMap<>(record);
+                    unitRecord.putAll(unitValues);
+                    unitRecords.add(unitRecord);
+                }
+
+                CTTbl table = body.addNewTbl();
+                table.set(templateTable);
+                clearTablePosition(table);
+                renderTable(table, unitValues, unitRecords);
+                int headerRowCount = table.sizeOfTrArray() - unitRecords.size();
+                configureUnitTablePagination(table, renderedPage > 0, headerRowCount);
+                configureUnitColumnVerticalMerge(table);
+                renderedPage++;
+            }
+        }
+    }
+
+    private List<List<Map<String, String>>> paginateRecords(CTTbl templateTable,
+                                                             List<Map<String, String>> records,
+                                                             CTSectPr section) {
+        List<List<Map<String, String>>> pages = new ArrayList<>();
+        if (records.isEmpty() || templateTable.sizeOfTrArray() == 0) {
+            return pages;
+        }
+
+        List<Integer> columnWidths = tableColumnWidths(templateTable);
+        int dataRowIndex = templateTable.sizeOfTrArray() - 1;
+        int headerHeight = 0;
+        for (int i = 0; i < dataRowIndex; i++) {
+            headerHeight += estimateRowHeight(templateTable.getTrArray(i), columnWidths, records.get(0));
+        }
+
+        int availableHeight = Math.max(1200,
+                (int) ((pageContentHeight(section) - headerHeight) * 0.50));
+        List<Map<String, String>> current = new ArrayList<>();
+        int usedHeight = 0;
+        for (Map<String, String> record : records) {
+            int rowHeight = estimateRowHeight(templateTable.getTrArray(dataRowIndex), columnWidths, record);
+            if (!current.isEmpty() && usedHeight + rowHeight > availableHeight) {
+                pages.add(current);
+                current = new ArrayList<>();
+                usedHeight = 0;
+            }
+            current.add(record);
+            usedHeight += rowHeight;
+        }
+        if (!current.isEmpty()) {
+            pages.add(current);
+        }
+        return pages;
+    }
+
+    private List<Integer> tableColumnWidths(CTTbl table) {
+        List<Integer> widths = new ArrayList<>();
+        CTTblGrid grid = table.getTblGrid();
+        if (grid != null) {
+            for (int i = 0; i < grid.sizeOfGridColArray(); i++) {
+                Object value = grid.getGridColArray(i).getW();
+                widths.add(value == null ? 0 : new BigInteger(String.valueOf(value)).intValue());
+            }
+        }
+        if (widths.isEmpty()) {
+            for (int width : TABLE_COLUMN_WIDTHS) {
+                widths.add(width);
+            }
+        }
+        return widths;
+    }
+
+    private int pageContentHeight(CTSectPr section) {
+        int height = A4_LANDSCAPE_HEIGHT.intValue();
+        int top = PAGE_MARGIN.intValue();
+        int bottom = PAGE_MARGIN.intValue();
+        if (section != null) {
+            if (section.isSetPgSz() && section.getPgSz().getH() != null) {
+                height = new BigInteger(String.valueOf(section.getPgSz().getH())).intValue();
+            }
+            if (section.isSetPgMar()) {
+                if (section.getPgMar().getTop() != null) {
+                    top = new BigInteger(String.valueOf(section.getPgMar().getTop())).intValue();
+                }
+                if (section.getPgMar().getBottom() != null) {
+                    bottom = new BigInteger(String.valueOf(section.getPgMar().getBottom())).intValue();
+                }
+            }
+        }
+        return Math.max(1200, height - top - bottom);
+    }
+
+    private int estimateRowHeight(CTRow row, List<Integer> columnWidths, Map<String, String> values) {
+        int maxHeight = 0;
+        int gridIndex = 0;
+        for (CTTc cell : row.getTcList()) {
+            int gridSpan = 1;
+            if (cell.isSetTcPr() && cell.getTcPr().isSetGridSpan()
+                    && cell.getTcPr().getGridSpan().getVal() != null) {
+                gridSpan = new BigInteger(String.valueOf(cell.getTcPr().getGridSpan().getVal())).intValue();
+            }
+            int cellWidth = cellWidthTwips(cell, columnWidths, gridIndex, gridSpan);
+            int cellHeight = 0;
+            for (CTP paragraph : cell.getPList()) {
+                String paragraphText = substitute(paragraphText(paragraph), values);
+                double fontSize = paragraphFontSize(paragraph, 9.0);
+                double usableWidth = Math.max(8.0, cellWidth / 20.0 - 2.0);
+                int lines = Math.max(1, (int) Math.ceil(textUnits(paragraphText) * fontSize / usableWidth));
+                cellHeight += (int) Math.ceil(lines * fontSize * 1.28 * 20.0) + 20;
+            }
+            maxHeight = Math.max(maxHeight, cellHeight);
+            gridIndex += gridSpan;
+        }
+        return Math.max(240, maxHeight);
+    }
+
+    private int cellWidthTwips(CTTc cell, List<Integer> columnWidths, int gridIndex, int gridSpan) {
+        if (cell.isSetTcPr() && cell.getTcPr().isSetTcW()) {
+            Object width = cell.getTcPr().getTcW().getW();
+            if (width != null && (!cell.getTcPr().getTcW().isSetType()
+                    || cell.getTcPr().getTcW().getType() == STTblWidth.DXA)) {
+                return new BigInteger(String.valueOf(width)).intValue();
+            }
+        }
+        int width = 0;
+        for (int i = gridIndex; i < gridIndex + gridSpan && i < columnWidths.size(); i++) {
+            width += columnWidths.get(i);
+        }
+        return width > 0 ? width : 1000;
+    }
+
+    private String paragraphText(CTP paragraph) {
+        StringBuilder builder = new StringBuilder();
+        for (CTR run : paragraph.getRList()) {
+            for (CTText text : run.getTList()) {
+                builder.append(text.getStringValue());
+            }
+        }
+        return builder.toString();
+    }
+
+    private double paragraphFontSize(CTP paragraph, double fallback) {
+        for (CTR run : paragraph.getRList()) {
+            if (run.isSetRPr() && run.getRPr().isSetSz() && run.getRPr().getSz().getVal() != null) {
+                return new BigInteger(String.valueOf(run.getRPr().getSz().getVal())).intValue() / 2.0;
+            }
+        }
+        return fallback;
+    }
+
+    private double textUnits(String value) {
+        double units = 0;
+        for (int i = 0; i < value.length(); i++) {
+            char character = value.charAt(i);
+            if (character == '\r' || character == '\n') {
+                continue;
+            }
+            if (character == ' ') {
+                units += 0.5;
+            } else if (character >= 0x2e80 || Character.isIdeographic(character)) {
+                units += 1.0;
+            } else {
+                units += 0.58;
+            }
+        }
+        return units;
+    }
+
+    private void renderInterleavedUnitTable(CTBody body, CTTbl templateTable,
+                                              List<Map<String, String>> records,
+                                              Map<Integer, String> unitNames) {
+        List<Map<String, String>> repeatedRecords = repeatRecordsByUnit(records, unitNames);
+        CTTbl table = body.addNewTbl();
+        table.set(templateTable);
+        clearTablePosition(table);
+        Map<String, String> headerValues = new LinkedHashMap<>(records.get(0));
+        headerValues.put("unitDisplay", "鑱斿彿/鑱斿悕");
+        renderTable(table, headerValues, repeatedRecords);
+        configureRecordRows(table, table.sizeOfTrArray() - repeatedRecords.size());
+    }
+
+    private List<Map<String, String>> repeatRecordsByUnit(List<Map<String, String>> records,
+                                                        Map<Integer, String> unitNames) {
+        List<Map<String, String>> repeatedRecords = new ArrayList<>();
+        for (Map<String, String> record : records) {
+            for (int unit = 1; unit <= UNIT_COUNT; unit++) {
+                Map<String, String> values = new LinkedHashMap<>(record);
+                String unitName = text(unitNames.get(unit)).trim();
+                values.put("unitNo", String.valueOf(unit));
+                values.put("unitName", unitName);
+                values.put("unitDisplay", "绗�" + unit + "鑱�"
+                        + (StringUtils.hasText(unitName) ? "    " + unitName : ""));
+                repeatedRecords.add(values);
+            }
+        }
+        return repeatedRecords;
+    }
+
+    private void renderLegacyGroupedUnits(XWPFDocument document,
+                                           List<Map<String, String>> records,
+                                           Map<Integer, String> unitNames) {
+        CTBody body = document.getDocument().getBody();
+        List<XmlObject> templateElements = snapshotBodyElements(document);
+        if (templateElements.isEmpty()) {
+            throw new BusinessException("Engineering order template is empty: " + templateLocation);
+        }
+
+        int detailTableIndex = findDetailTableIndex(templateElements);
+        if (detailTableIndex < 0) {
+            throw new BusinessException("Engineering order template has no detail table: " + templateLocation);
+        }
+        int bottomTableIndex = findBottomTableIndex(templateElements, detailTableIndex);
+
+        Map<String, String> shared = new LinkedHashMap<>(records.get(0));
+        // The first and last template tables are page chrome. Put them into the
+        // Word header/footer so Word repeats them on every generated page.
+        copyTemplatePrefixToHeader(document, templateElements, detailTableIndex, shared, records);
+        if (bottomTableIndex >= 0) {
+            copyTemplateTableToFooter(document, (CTTbl) templateElements.get(bottomTableIndex), shared, records);
+        }
+        CTSectPr section = body.isSetSectPr() ? (CTSectPr) body.getSectPr().copy() : null;
+        CTBody cleanBody = CTBody.Factory.newInstance();
+        if (section != null) {
+            cleanBody.addNewSectPr().set(section);
+        }
+        body.set(cleanBody);
+
+        for (int unit = 1; unit <= UNIT_COUNT; unit++) {
+            if (unit > 1) {
+                appendPageBreakParagraph(body);
+            }
+
+            String unitName = text(unitNames.get(unit)).trim();
+            Map<String, String> unitValues = new LinkedHashMap<>();
+            unitValues.put("unitNo", String.valueOf(unit));
+            unitValues.put("unitName", unitName);
+            unitValues.put("unitDisplay", "绗�" + unit + "鑱�"
+                    + (StringUtils.hasText(unitName) ? "    " + unitName : ""));
+
+            Map<String, String> values = new LinkedHashMap<>(shared);
+            values.putAll(unitValues);
+
+            List<Map<String, String>> unitRecords = new ArrayList<>();
+            for (Map<String, String> record : records) {
+                Map<String, String> unitRecord = new LinkedHashMap<>(record);
+                unitRecord.putAll(unitValues);
+                unitRecords.add(unitRecord);
+            }
+
+            CTTbl table = body.addNewTbl();
+            table.set(templateElements.get(detailTableIndex));
+            clearTablePosition(table);
+            renderTable(table, values, unitRecords);
+            configureRecordRows(table, table.sizeOfTrArray() - unitRecords.size());
+            configureUnitColumnVerticalMerge(table);
+        }
+
+        moveSectionPropertiesToEnd(body);
+    }
+
+    private List<XmlObject> snapshotBodyElements(XWPFDocument document) {
+        List<XmlObject> elements = new ArrayList<>();
+        for (Object element : document.getBodyElements()) {
+            if (element instanceof XWPFParagraph) {
+                elements.add(((XWPFParagraph) element).getCTP().copy());
+            } else if (element instanceof XWPFTable) {
+                elements.add(((XWPFTable) element).getCTTbl().copy());
+            }
+        }
+        return elements;
+    }
+
+    private int findDetailTableIndex(List<XmlObject> elements) {
+        for (int i = 0; i < elements.size(); i++) {
+            XmlObject element = elements.get(i);
+            if (element instanceof CTTbl && containsDetailPlaceholder((CTTbl) element)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private int findBottomTableIndex(List<XmlObject> elements, int detailTableIndex) {
+        int bottomTableIndex = -1;
+        for (int i = detailTableIndex + 1; i < elements.size(); i++) {
+            if (elements.get(i) instanceof CTTbl) {
+                bottomTableIndex = i;
+            }
+        }
+        return bottomTableIndex;
+    }
+
+    private void renderDetailTables(CTBody body, CTTbl table,
+                                   List<Map<String, String>> records,
+                                   Map<Integer, String> unitNames) {
+        int rowCount = table.sizeOfTrArray();
+        if (rowCount == 0) {
+            return;
+        }
+
+        int headerRowCount = rowCount - 1;
+        List<CTRow> headerRows = new ArrayList<>();
+        for (int i = 0; i < headerRowCount; i++) {
+            headerRows.add((CTRow) table.getTrArray(i).copy());
+        }
+        CTRow dataRow = (CTRow) table.getTrArray(rowCount - 1).copy();
+
+        CTTbl rowTemplate = (CTTbl) table.copy();
+        while (rowTemplate.sizeOfTrArray() > 0) {
+            rowTemplate.removeTr(0);
+        }
+
+        // Each unit is one continuously paginated table. Word fits as many detail
+        // rows per page as their actual rendered heights allow; cantSplit keeps each
+        // individual detail row intact across page boundaries.
+        for (int unit = 1; unit <= UNIT_COUNT; unit++) {
+            String unitName = text(unitNames.get(unit)).trim();
+            Map<String, String> unitValues = new LinkedHashMap<>();
+            unitValues.put("unitNo", String.valueOf(unit));
+            unitValues.put("unitName", unitName);
+            unitValues.put("unitDisplay", "\u7b2c" + unit + "\u8054"
+                    + (StringUtils.hasText(unitName) ? "    " + unitName : ""));
+
+            CTTbl unitTable = (CTTbl) rowTemplate.copy();
+            for (CTRow headerRow : headerRows) {
+                CTRow renderedHeader = unitTable.addNewTr();
+                renderedHeader.set(headerRow);
+                replaceInRow(renderedHeader, unitValues);
+            }
+
+            for (Map<String, String> record : records) {
+                Map<String, String> values = new LinkedHashMap<>(record);
+                values.putAll(unitValues);
+                CTRow renderedData = unitTable.addNewTr();
+                renderedData.set(dataRow);
+                replaceInRow(renderedData, values);
+            }
+
+            configureUnitTablePagination(unitTable, unit > 1, headerRows.size());
+            body.addNewTbl().set(unitTable);
+        }
+    }
+
+    private void configureUnitTablePagination(CTTbl table, boolean pageBreakBefore, int headerRowCount) {
+        configureRecordRows(table, headerRowCount);
+        if (!pageBreakBefore || table.sizeOfTrArray() == 0) {
+            return;
+        }
+        CTRow firstRow = table.getTrArray(0);
+        if (firstRow.sizeOfTcArray() == 0) {
+            return;
+        }
+        CTTc firstCell = firstRow.getTcArray(0);
+        if (firstCell.sizeOfPArray() == 0) {
+            firstCell.addNewP();
+        }
+        setPageBreakBefore(firstCell.getPArray(0));
+    }
+
+    /** Merge the final unit-number column across every row in one unit table. */
+    private void configureUnitColumnVerticalMerge(CTTbl table) {
+        int rowCount = table.sizeOfTrArray();
+        for (int i = 0; i < rowCount; i++) {
+            CTRow row = table.getTrArray(i);
+            if (row.sizeOfTcArray() == 0) {
+                continue;
+            }
+            CTTc unitCell = row.getTcArray(row.sizeOfTcArray() - 1);
+            CTTcPr properties = unitCell.isSetTcPr() ? unitCell.getTcPr() : unitCell.addNewTcPr();
+            if (properties.isSetVMerge()) {
+                properties.unsetVMerge();
+            }
+            properties.addNewVMerge().setVal(i == 0 ? STMerge.RESTART : STMerge.CONTINUE);
+            if (!properties.isSetTextDirection()) {
+                properties.addNewTextDirection();
+            }
+            properties.getTextDirection().setVal(STTextDirection.BT_LR);
+            if (!properties.isSetVAlign()) {
+                properties.addNewVAlign();
+            }
+            properties.getVAlign().setVal(STVerticalJc.CENTER);
+        }
+    }
+
+    private void configureRecordRows(CTTbl table, int headerRowCount) {
+        clearTablePosition(table);
+        for (int i = 0; i < table.sizeOfTrArray(); i++) {
+            CTRow row = table.getTrArray(i);
+            CTTrPr properties = row.isSetTrPr() ? row.getTrPr() : row.addNewTrPr();
+            if (properties.sizeOfCantSplitArray() == 0) {
+                properties.addNewCantSplit();
+            }
+            while (properties.sizeOfTblHeaderArray() > 0) {
+                properties.removeTblHeader(0);
+            }
+            if (i < headerRowCount) {
+                properties.addNewTblHeader();
+            }
+            configureCellParagraphPagination(row, false);
+        }
+    }
+
+    private int tableWidth(CTTbl table) {
+        CTTblGrid grid = table.getTblGrid();
+        if (grid != null) {
+            int width = 0;
+            for (int i = 0; i < grid.sizeOfGridColArray(); i++) {
+                Object value = grid.getGridColArray(i).getW();
+                if (value != null) {
+                    width += new BigInteger(String.valueOf(value)).intValue();
+                }
+            }
+            if (width > 0) {
+                return width;
+            }
+        }
+        return totalWidth(TABLE_COLUMN_WIDTHS);
+    }
+
+    private void noBorder(org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBorder border) {
+        border.setVal(STBorder.NIL);
+        border.setSz(BigInteger.ZERO);
+        border.setSpace(BigInteger.ZERO);
+    }
+
+    private void zeroMargin(CTTblWidth margin) {
+        margin.setType(STTblWidth.DXA);
+        margin.setW(BigInteger.ZERO);
+    }
+
+    private void configureCellParagraphPagination(CTRow row, boolean keepWithNext) {
+        // Keep the complete row together. Word measures the final rendered text height dynamically,
+        // so no fixed row height or per-page record count is needed.
+        for (CTTc cell : row.getTcList()) {
+            for (CTP paragraph : cell.getPList()) {
+                CTPPr properties = paragraph.isSetPPr() ? paragraph.getPPr() : paragraph.addNewPPr();
+                if (!properties.isSetKeepLines()) {
+                    properties.addNewKeepLines();
+                }
+                if (properties.isSetKeepNext()) {
+                    properties.unsetKeepNext();
+                }
+                if (keepWithNext) {
+                    properties.addNewKeepNext();
+                }
+            }
+        }
+    }
+
+    private void copyTemplatePrefixToHeader(XWPFDocument document, List<XmlObject> elements,
+                                             int detailTableIndex, Map<String, String> values,
+                                             List<Map<String, String>> records) {
+        XWPFHeader header = document.createHeader(HeaderFooterType.DEFAULT);
+        CTHdrFtr headerContent = header._getHdrFtr();
+        for (int i = 0; i < detailTableIndex; i++) {
+            XmlObject element = elements.get(i);
+            if (element instanceof CTP) {
+                CTP paragraph = headerContent.addNewP();
+                paragraph.set(element);
+                replaceInParagraph(paragraph, values);
+            } else if (element instanceof CTTbl) {
+                appendHeaderFooterTable(headerContent, (CTTbl) element, values, records);
+            }
+        }
+    }
+
+    private void copyTemplateTableToFooter(XWPFDocument document, CTTbl source,
+                                           Map<String, String> values, List<Map<String, String>> records) {
+        XWPFFooter footer = document.createFooter(HeaderFooterType.DEFAULT);
+        appendHeaderFooterTable(footer._getHdrFtr(), source, values, records);
+    }
+
+    private void appendHeaderFooterTable(CTHdrFtr content, CTTbl source,
+                                        Map<String, String> values,
+                                        List<Map<String, String>> records) {
+        CTTbl table = content.addNewTbl();
+        table.set(source);
+        clearTablePosition(table);
+        renderTable(table, values, records);
+    }
+
+    private void clearTablePosition(CTTbl table) {
+        CTTblPr properties = table.getTblPr() != null ? table.getTblPr() : table.addNewTblPr();
+        if (properties.isSetTblpPr()) {
+            properties.unsetTblpPr();
+        }
+    }
+
+    private void renderTable(CTTbl table, Map<String, String> unitValues,
+                             List<Map<String, String>> records) {
+        boolean expand = containsDetailPlaceholder(table);
+        int rowCount = table.sizeOfTrArray();
+        if (rowCount == 0) {
+            return;
+        }
+        if (expand) {
+            CTRow dataRow = table.getTrArray(rowCount - 1);
+            for (CTTc cell : dataRow.getTcList()) {
+                CTTcPr properties = cell.isSetTcPr() ? cell.getTcPr() : cell.addNewTcPr();
+                if (properties.isSetVMerge()) {
+                    properties.unsetVMerge();
+                }
+            }
+        }
+
+        int dataRowIndex = rowCount - 1;
+        if (expand) {
+            CTRow dataRow = table.getTrArray(dataRowIndex);
+            for (int i = 0; i < records.size(); i++) {
+                table.addNewTr().set(dataRow);
+            }
+            table.removeTr(dataRowIndex);
+        }
+        for (int i = 0; i < table.sizeOfTrArray(); i++) {
+            boolean dataRow = expand && i >= table.sizeOfTrArray() - records.size();
+            Map<String, String> values = dataRow
+                    ? records.get(i - (table.sizeOfTrArray() - records.size()))
+                    : unitValues;
+            replaceInRow(table.getTrArray(i), values);
+        }
+    }
+
+    private boolean containsDetailPlaceholder(CTTbl table) {
+        StringBuilder builder = new StringBuilder();
+        for (CTRow row : table.getTrList()) {
+            for (CTTc cell : row.getTcList()) {
+                for (CTP paragraph : cell.getPList()) {
+                    for (CTR run : paragraph.getRList()) {
+                        for (CTText text : run.getTList()) {
+                            builder.append(text.getStringValue());
+                        }
+                    }
+                }
+            }
+        }
+
+        Matcher matcher = TEMPLATE_PLACEHOLDER.matcher(builder.toString());
+        while (matcher.find()) {
+            String key = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
+            if (DETAIL_PLACEHOLDERS.contains(key.trim())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void replaceInRow(CTRow row, Map<String, String> values) {
+        for (CTTc cell : row.getTcList()) {
+            for (CTP paragraph : cell.getPList()) {
+                replaceInParagraph(paragraph, values);
+            }
+        }
+    }
+
+    private void replaceInParagraph(CTP paragraph, Map<String, String> values) {
+        List<CTR> runs = paragraph.getRList();
+        if (runs.isEmpty()) {
+            return;
+        }
+
+        List<TextChunk> chunks = new ArrayList<>();
+        StringBuilder builder = new StringBuilder();
+        for (CTR run : runs) {
+            for (CTText text : run.getTList()) {
+                String value = text.getStringValue();
+                if (value == null) {
+                    value = "";
+                }
+                chunks.add(new TextChunk(text, builder.length(), builder.length() + value.length()));
+                builder.append(value);
+            }
+        }
+
+        String original = builder.toString();
+        if (original.indexOf('{') < 0) {
+            return;
+        }
+
+        Map<CTText, List<TextEdit>> edits = new LinkedHashMap<>();
+        Matcher matcher = TEMPLATE_PLACEHOLDER.matcher(original);
+        while (matcher.find()) {
+            String key = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
+            String replacement = values.get(key.trim());
+            if (replacement == null) {
+                replacement = "";
+            }
+            for (TextChunk chunk : chunks) {
+                int overlapStart = Math.max(matcher.start(), chunk.start);
+                int overlapEnd = Math.min(matcher.end(), chunk.end);
+                if (overlapStart >= overlapEnd) {
+                    continue;
+                }
+                String inserted = overlapStart == matcher.start() ? replacement : "";
+                edits.computeIfAbsent(chunk.text, item -> new ArrayList<>())
+                        .add(new TextEdit(overlapStart - chunk.start, overlapEnd - chunk.start, inserted));
+            }
+        }
+
+        for (Map.Entry<CTText, List<TextEdit>> entry : edits.entrySet()) {
+            String value = text(entry.getKey().getStringValue());
+            entry.getValue().sort((left, right) -> Integer.compare(right.start, left.start));
+            for (TextEdit edit : entry.getValue()) {
+                value = value.substring(0, edit.start) + edit.replacement + value.substring(edit.end);
+            }
+            entry.getKey().setStringValue(value);
+        }
+    }
+
+    private static final class TextChunk {
+        private final CTText text;
+        private final int start;
+        private final int end;
+
+        private TextChunk(CTText text, int start, int end) {
+            this.text = text;
+            this.start = start;
+            this.end = end;
+        }
+    }
+
+    private static final class TextEdit {
+        private final int start;
+        private final int end;
+        private final String replacement;
+
+        private TextEdit(int start, int end, String replacement) {
+            this.start = start;
+            this.end = end;
+            this.replacement = replacement;
+        }
+    }
+
+    private void replaceInParagraphLegacy(CTP paragraph, Map<String, String> values) {
+        List<CTR> runs = paragraph.getRList();
+        if (runs.isEmpty()) {
+            return;
+        }
+        StringBuilder builder = new StringBuilder();
+        for (CTR run : runs) {
+            for (CTText text : run.getTList()) {
+                builder.append(text.getStringValue());
+            }
+        }
+        String original = builder.toString();
+        if (original.indexOf('{') < 0) {
+            return;
+        }
+        String replaced = substitute(original, values);
+        if (replaced.equals(original)) {
+            return;
+        }
+        if (runs.size() == 1) {
+            CTR run = runs.get(0);
+            if (run.sizeOfTArray() > 0) {
+                run.getTArray(0).setStringValue(replaced);
+                for (int i = run.sizeOfTArray() - 1; i >= 1; i--) {
+                    run.removeT(i);
+                }
+                return;
+            }
+        }
+        for (CTR run : runs) {
+            for (int i = run.sizeOfTArray() - 1; i >= 0; i--) {
+                run.removeT(i);
+            }
+            for (int i = run.sizeOfTabArray() - 1; i >= 0; i--) {
+                run.removeTab(i);
+            }
+            for (int i = run.sizeOfBrArray() - 1; i >= 0; i--) {
+                run.removeBr(i);
+            }
+        }
+        runs.get(0).addNewT().setStringValue(replaced);
+    }
+
+    private String substitute(String text, Map<String, String> values) {
+        Matcher matcher = PLACEHOLDER.matcher(text);
+        StringBuffer buffer = new StringBuffer();
+        while (matcher.find()) {
+            String key = matcher.group(1).trim();
+            String value = values.get(key);
+            matcher.appendReplacement(buffer, Matcher.quoteReplacement(value == null ? "" : value));
+        }
+        matcher.appendTail(buffer);
+        return buffer.toString();
+    }
+
+    private void appendPageBreakParagraph(CTBody body) {
+        CTP paragraph = body.addNewP();
+        CTPPr properties = paragraph.addNewPPr();
+        properties.addNewPageBreakBefore();
+        properties.addNewSpacing().setBefore(BigInteger.ZERO);
+        properties.getSpacing().setAfter(BigInteger.ZERO);
+        properties.addNewRPr().addNewSz().setVal(BigInteger.valueOf(2));
+    }
+
+    private void setPageBreakBefore(CTP paragraph) {
+        CTPPr properties = paragraph.isSetPPr() ? paragraph.getPPr() : paragraph.addNewPPr();
+        if (!properties.isSetPageBreakBefore()) {
+            properties.addNewPageBreakBefore();
+        }
+    }
+
+    private void moveSectionPropertiesToEnd(CTBody body) {
+        if (!body.isSetSectPr()) {
+            return;
+        }
+        CTSectPr section = (CTSectPr) body.getSectPr().copy();
+        body.unsetSectPr();
+        body.setSectPr(section);
+    }
+
+    private String collectText(XWPFDocument document) {
+        StringBuilder builder = new StringBuilder();
+        for (XWPFParagraph paragraph : document.getParagraphs()) {
+            builder.append(paragraph.getText()).append('\n');
+        }
+        for (XWPFTable table : document.getTables()) {
+            for (XWPFTableRow row : table.getRows()) {
+                for (XWPFTableCell cell : row.getTableCells()) {
+                    builder.append(cell.getText()).append('\n');
+                }
+            }
+        }
+        return builder.toString();
+    }
+
+    private InputStream openTemplate() throws IOException {
+        Path path = templatePath();
+        if (path != null && Files.exists(path)) {
+            return Files.newInputStream(path);
+        }
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        try (XWPFDocument document = buildDefaultTemplate()) {
+            document.write(buffer);
+        }
+        return new ByteArrayInputStream(buffer.toByteArray());
+    }
+
+    private Path templatePath() {
+        if (!StringUtils.hasText(templateLocation)) {
+            return null;
+        }
+        return Paths.get(templateLocation).toAbsolutePath().normalize();
+    }
+
+
+    /** 鐢熸垚涓�浠藉甫鍗犱綅绗︾殑榛樿宸ョ▼鍗曟ā鏉裤�� */
+    XWPFDocument buildDefaultTemplate() throws IOException {
+        XWPFDocument document = new XWPFDocument();
+        CTBody body = document.getDocument().getBody();
+        CTSectPr section = body.isSetSectPr() ? body.getSectPr() : body.addNewSectPr();
+        CTPageSz pageSize = section.isSetPgSz() ? section.getPgSz() : section.addNewPgSz();
+        pageSize.setW(A4_LANDSCAPE_WIDTH);
+        pageSize.setH(A4_LANDSCAPE_HEIGHT);
+        pageSize.setOrient(STPageOrientation.LANDSCAPE);
+        CTPageMar margin = section.isSetPgMar() ? section.getPgMar() : section.addNewPgMar();
+        margin.setTop(PAGE_MARGIN);
+        margin.setBottom(PAGE_MARGIN);
+        margin.setLeft(PAGE_MARGIN);
+        margin.setRight(PAGE_MARGIN);
+        int mainColumns = DEFAULT_COLUMNS.length;
+        int columns = mainColumns + 1;
+        XWPFTable table = document.createTable(3, columns);
+        applyBorders(table.getCTTbl());
+        applyGrid(table.getCTTbl(), TABLE_COLUMN_WIDTHS);
+
+        XWPFTableRow headerRow = table.getRow(0);
+        mergeMainColumns(headerRow, mainColumns, document);
+        setCellText(headerRow.getCell(0), DEFAULT_UNIT_HEADER, true);
+
+        XWPFTableRow titleRow = table.getRow(1);
+        XWPFTableRow fieldRow = table.getRow(2);
+        for (int i = 0; i < mainColumns; i++) {
+            setWidth(titleRow.getCell(i), TABLE_COLUMN_WIDTHS[i]);
+            setWidth(fieldRow.getCell(i), TABLE_COLUMN_WIDTHS[i]);
+            setCellText(titleRow.getCell(i), DEFAULT_COLUMNS[i][0], true);
+            setCellText(fieldRow.getCell(i), DEFAULT_COLUMNS[i][1], false);
+        }
+        configureUnitColumn(titleRow, mainColumns, false);
+        configureUnitColumn(fieldRow, mainColumns, false);
+
+        moveSectionPropertiesToEnd(body);
+        document.getProperties().getCoreProperties().setKeywords(TEMPLATE_VERSION);
+        return document;
+    }
+
+    private void applyBorders(CTTbl table) {
+        CTTblPr properties = table.getTblPr() != null ? table.getTblPr() : table.addNewTblPr();
+        CTTblBorders borders = properties.isSetTblBorders() ? properties.getTblBorders() : properties.addNewTblBorders();
+        border(borders.isSetTop() ? borders.getTop() : borders.addNewTop());
+        border(borders.isSetLeft() ? borders.getLeft() : borders.addNewLeft());
+        border(borders.isSetBottom() ? borders.getBottom() : borders.addNewBottom());
+        border(borders.isSetRight() ? borders.getRight() : borders.addNewRight());
+        border(borders.isSetInsideH() ? borders.getInsideH() : borders.addNewInsideH());
+        border(borders.isSetInsideV() ? borders.getInsideV() : borders.addNewInsideV());
+    }
+
+    private void border(org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBorder border) {
+        border.setVal(STBorder.SINGLE);
+        border.setSz(BigInteger.valueOf(4));
+        border.setColor("000000");
+        border.setSpace(BigInteger.ZERO);
+    }
+
+    private void applyGrid(CTTbl table, int[] widths) {
+        CTTblGrid grid = table.getTblGrid() != null ? table.getTblGrid() : table.addNewTblGrid();
+        while (grid.sizeOfGridColArray() > widths.length) {
+            grid.removeGridCol(grid.sizeOfGridColArray() - 1);
+        }
+        while (grid.sizeOfGridColArray() < widths.length) {
+            grid.addNewGridCol();
+        }
+        for (int i = 0; i < widths.length; i++) {
+            grid.getGridColArray(i).setW(BigInteger.valueOf(widths[i]));
+        }
+        CTTblPr properties = table.getTblPr() != null ? table.getTblPr() : table.addNewTblPr();
+        CTTblWidth width = properties.isSetTblW() ? properties.getTblW() : properties.addNewTblW();
+        width.setType(STTblWidth.DXA);
+        width.setW(BigInteger.valueOf(totalWidth(widths)));
+    }
+
+    private int totalWidth(int[] widths) {
+        int total = 0;
+        for (int value : widths) {
+            total += value;
+        }
+        return total;
+    }
+
+    private void mergeMainColumns(XWPFTableRow row, int mainColumns, XWPFDocument document) {
+        CTRow ctRow = row.getCtRow();
+        while (ctRow.sizeOfTcArray() > 2) {
+            ctRow.removeTc(ctRow.sizeOfTcArray() - 2);
+        }
+        CTTc cell = ctRow.getTcArray(0);
+        CTTcPr properties = cell.isSetTcPr() ? cell.getTcPr() : cell.addNewTcPr();
+        if (!properties.isSetGridSpan()) {
+            properties.addNewGridSpan().setVal(BigInteger.valueOf(mainColumns));
+        }
+        CTTblWidth cellWidth = properties.isSetTcW() ? properties.getTcW() : properties.addNewTcW();
+        cellWidth.setType(STTblWidth.DXA);
+        cellWidth.setW(BigInteger.valueOf(totalWidth(TABLE_COLUMN_WIDTHS) - UNIT_COLUMN_WIDTH));
+
+        CTTc unitCell = ctRow.getTcArray(1);
+        XWPFTableCell unitWrapper = new XWPFTableCell(unitCell, row, document);
+        setWidth(unitWrapper, UNIT_COLUMN_WIDTH);
+        configureUnitCell(unitCell, true);
+        setCellText(unitWrapper, "{unitDisplay}", true);
+    }
+
+    private void configureUnitColumn(XWPFTableRow row, int mainColumns, boolean restart) {
+        int index = mainColumns;
+        XWPFTableCell cell = row.getCell(index);
+        setWidth(cell, UNIT_COLUMN_WIDTH);
+        configureUnitCell(cell.getCTTc(), restart);
+    }
+
+    private void configureUnitCell(CTTc cell, boolean restart) {
+        CTTcPr properties = cell.isSetTcPr() ? cell.getTcPr() : cell.addNewTcPr();
+        if (!properties.isSetVMerge()) {
+            properties.addNewVMerge();
+        }
+        properties.getVMerge().setVal(restart ? STMerge.RESTART : STMerge.CONTINUE);
+        if (!properties.isSetTextDirection()) {
+            properties.addNewTextDirection();
+        }
+        properties.getTextDirection().setVal(STTextDirection.BT_LR);
+        if (!properties.isSetVAlign()) {
+            properties.addNewVAlign();
+        }
+        properties.getVAlign().setVal(STVerticalJc.CENTER);
+    }
+
+    private void setWidth(XWPFTableCell cell, int width) {
+        CTTcPr properties = cell.getCTTc().isSetTcPr() ? cell.getCTTc().getTcPr() : cell.getCTTc().addNewTcPr();
+        CTTblWidth cellWidth = properties.isSetTcW() ? properties.getTcW() : properties.addNewTcW();
+        cellWidth.setType(STTblWidth.DXA);
+        cellWidth.setW(BigInteger.valueOf(width));
+    }
+
+    private void setCellText(XWPFTableCell cell, String text, boolean centered) {
+        XWPFParagraph paragraph = cell.getParagraphs().isEmpty() ? cell.addParagraph() : cell.getParagraphs().get(0);
+        if (centered) {
+            paragraph.setAlignment(org.apache.poi.xwpf.usermodel.ParagraphAlignment.CENTER);
+        }
+        org.apache.poi.xwpf.usermodel.XWPFRun run = paragraph.createRun();
+        run.setFontFamily("SimSun");
+        run.setFontSize(9);
+        run.setText(text);
+        for (XWPFParagraph extra : new ArrayList<>(cell.getParagraphs())) {
+            if (extra != paragraph) {
+                cell.removeParagraph(cell.getParagraphs().indexOf(extra));
+            }
+        }
+    }
+
+
+    private static String text(String value) {
+        return value == null ? "" : value;
+    }
+
+    private static String date(LocalDate value) {
+        return value == null ? "" : DATE_FORMAT.format(value);
+    }
+
+    private static String yesNo(Integer value) {
+        return value != null && value == 1 ? "鏄�" : "鍚�";
+    }
+
+    private static String firstText(String... candidates) {
+        for (String candidate : candidates) {
+            if (StringUtils.hasText(candidate)) {
+                return candidate;
+            }
+        }
+        return "";
+    }
+}
diff --git a/emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java b/emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java
index bd2bd90..36dd95e 100644
--- a/emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java
+++ b/emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java
@@ -56,6 +56,10 @@
                 def("fault:completion:query", "\u67e5\u8be2"),
                 def("fault:completion:status", "\u5b8c\u5de5\u72b6\u6001\u8bbe\u7f6e"),
                 def("fault:completion:score", "rk_T\u5458\u8bc4\u5206"));
+        put("/fault/engineering-print",
+                def("fault:engineeringPrint:query", "\u67e5\u8be2"),
+                def("fault:engineeringPrint:print", "\u6253\u5370"),
+                def("fault:engineeringPrint:print2", "\u6253\u5370\u5de5\u7a0b\u53552"));
         put("/completion/report",
                 def("completion:report:save", "\u4fdd\u5b58"),
                 def("completion:report:submit", "\u63d0\u4ea4"));
diff --git a/emergency-repair-server/src/main/resources/application.yml b/emergency-repair-server/src/main/resources/application.yml
index f1daa32..2eecb7a 100644
--- a/emergency-repair-server/src/main/resources/application.yml
+++ b/emergency-repair-server/src/main/resources/application.yml
@@ -1,5 +1,3 @@
-fault-code:
-  prefix: "鍥哄畾鍓嶇紑-"
 app:
   seed-demo-data: false
 server:
@@ -11,6 +9,8 @@
       force: true
 
 spring:
+  config:
+    import: optional:file:./config/business-config.yml
   application:
     name: emergency-repair-server
   datasource:
diff --git a/emergency-repair-web/.env b/emergency-repair-web/.env
new file mode 100644
index 0000000..b475f6c
--- /dev/null
+++ b/emergency-repair-web/.env
@@ -0,0 +1,11 @@
+# 鍓嶇閰嶇疆锛堜慨鏀瑰悗闇�閲嶅惎 npm run serve / 閲嶆柊 npm run build 鐢熸晥锛�
+
+# 鍓嶇寮�鍙戞湇鍔″櫒绔彛
+PORT=8082
+
+# 鍓嶇璁块棶鐨勫悗绔湴鍧�锛堝紑鍙戠幆澧� /api 浠g悊鐩爣锛屾牸寮� http://ip:绔彛锛�
+VUE_APP_API_TARGET=http://127.0.0.1:8091
+
+# 鍓嶇璇锋眰鍚庣鐨勫熀纭�璺緞锛涢粯璁� /api锛堣蛋寮�鍙戜唬鐞嗘垨閮ㄧ讲鏃剁殑鍙嶅悜浠g悊锛夛紝
+# 濡傞渶鍓嶇鐩磋繛鍚庣鍙敼涓� http://ip:绔彛/api
+VUE_APP_API_BASE_URL=/api
diff --git a/emergency-repair-web/public/dict.js b/emergency-repair-web/public/dict.js
index 3ec46dc..80203fb 100644
--- a/emergency-repair-web/public/dict.js
+++ b/emergency-repair-web/public/dict.js
@@ -5,14 +5,14 @@
 window.__APP_DICT__ = {
   taskStatuses: {
     UNREPORTED: { text: '鏈笂鎶�', type: 'info' },
-    REPORTED_XCB: { text: '宸蹭笂鎶k_XCB', type: 'warning' },
+    REPORTED_XCB: { text: '鏁呴殰宸蹭笂鎶�', type: 'warning' },
     RETURNED: { text: '涓婄骇鎵撳洖', type: 'danger' },
-    XCB_REVIEWED: { text: 'rk_XCB鍔╃悊瀹℃牳瀹屾垚', type: 'primary' },
-    CMB_APPROVED: { text: 'rk_CMB棰嗗瀹℃壒瀹屾垚', type: 'success' },
-    JCC_ASSISTANT_APPROVED: { text: 'rk_JCC鍔╃悊瀹℃壒瀹屾垚', type: 'success' },
-    JCC_DIRECTOR_APPROVED: { text: 'rk_JCC澶勯暱宸查槄', type: 'success' },
+    XCB_REVIEWED: { text: '绗竴瀹℃壒浜哄鏍稿畬鎴愬鏍稿畬鎴�', type: 'primary' },
+    CMB_APPROVED: { text: '绗簩瀹℃壒浜哄鎵瑰畬鎴�', type: 'success' },
+    JCC_ASSISTANT_APPROVED: { text: '绗笁瀹℃壒浜哄鎵瑰畬鎴�', type: 'success' },
+    JCC_DIRECTOR_APPROVED: { text: '绗洓瀹℃壒浜哄凡闃�', type: 'success' },
     DISPATCHED: { text: '宸叉淳鍗�', type: 'primary' },
-    CANCELLED: { text: '宸查攢椤�', type: 'info' }
+    CANCELLED: { text: '閿�椤�', type: 'info' }
   },
   completionStatuses: {
     UNFINISHED: { text: '鏈畬宸�', type: 'info' },
diff --git a/emergency-repair-web/public/sensitive-words.json b/emergency-repair-web/public/sensitive-words.json
index 9c7c1ab..7d19ca6 100644
--- a/emergency-repair-web/public/sensitive-words.json
+++ b/emergency-repair-web/public/sensitive-words.json
@@ -2,7 +2,7 @@
   "sensitive_words": [
     {
       "codename": "rk_SystemName",
-      "real_name": "涓存姠淇鐞嗙郴缁�",
+      "real_name": "缁翠慨绠$悊绯荤粺",
       "scope": "ui",
       "note": "System title shown in the login page and application header."
     },
@@ -14,56 +14,55 @@
     },
     {
       "codename": "rk_X鍙�",
-      "real_name": "鍘垮彿",
+      "real_name": "绾垮彿",
       "scope": "ui",
       "note": "Development mode displays the codename. Replace real_name only in the deployed production dictionary."
     },
-    {
-      "codename": "rk_JC",
-      "real_name": "JC",
+	{
+      "codename": "rk_绗竴瀹℃壒浜�",
+      "real_name": "绗竴瀹℃壒浜�",
       "scope": "ui",
-      "note": "JC淇℃伅 = JC information."
+      "note": "XCB assistant review and dispatch."
     },
     {
-      "codename": "rk_JCC",
-      "real_name": "JCC",
+      "codename": "rk_绗簩瀹℃壒浜�",
+      "real_name": "绗簩瀹℃壒浜�",
+      "scope": "ui",
+      "note": "CMB leader approval role."
+    },	
+	{
+      "codename": "rk_绗笁瀹℃壒浜�",
+      "real_name": "绗笁瀹℃壒浜�",
       "scope": "ui",
       "note": "JCC assistant, director, and acceptance roles."
     },
     {
-      "codename": "rk_CMB",
-      "real_name": "CMB",
+      "codename": "rk_绗洓瀹℃壒浜�",
+      "real_name": "绗洓瀹℃壒浜�",
       "scope": "ui",
-      "note": "CMB leader approval role."
+      "note": "JCC assistant, director, and acceptance roles."
     },
     {
       "codename": "rk_TD",
-      "real_name": "TD",
+      "real_name": "姊槦",
       "scope": "ui",
       "note": "Home page statistic column."
     },
     {
       "codename": "rk_T",
-      "real_name": "T",
+      "real_name": "姊�",
       "scope": "ui",
       "note": "Word root. T闃熶笓涓�, T鍐呮柦宸�, and T鍛� all use the codename in development."
     },
     {
-      "codename": "rk_XCB",
-      "real_name": "淇埞鐗�",
-      "scope": "ui",
-      "note": "XCB assistant review and dispatch."
-    },
-    {
       "codename": "rk_H",
-      "real_name": "H",
+      "real_name": "榛�",
       "scope": "ui",
       "note": "Development mode displays H only. Replace real_name after production deployment."
     }
   ],
   "config": {
     "environment": "development",
-    "rule": "Build and deploy the normal development package. After deployment, manually replace the real_name values in the deployed dist/sensitive-words.json file in the production environment. Do not rebuild the frontend after that replacement.",
-    "code_pre": "ABC"
+    "rule": "Build and deploy the normal development package. After deployment, manually replace the real_name values in the deployed dist/sensitive-words.json file in the production environment. Do not rebuild the frontend after that replacement."
   }
 }
diff --git a/emergency-repair-web/src/api/index.js b/emergency-repair-web/src/api/index.js
index 3dc7b8d..56418a1 100644
--- a/emergency-repair-web/src/api/index.js
+++ b/emergency-repair-web/src/api/index.js
@@ -2,7 +2,7 @@
 import { Message } from 'element-ui'
 
 const api = axios.create({
-  baseURL: '/api',
+  baseURL: process.env.VUE_APP_API_BASE_URL,
   timeout: 20000
 })
 
diff --git a/emergency-repair-web/src/router/index.js b/emergency-repair-web/src/router/index.js
index f376755..ca86339 100644
--- a/emergency-repair-web/src/router/index.js
+++ b/emergency-repair-web/src/router/index.js
@@ -13,12 +13,13 @@
     children: [
       { path: 'dashboard', component: () => import('@/views/Dashboard.vue'), meta: { title: '棣栭〉闂ㄦ埛', cacheName: 'Dashboard' } },
       { path: 'fault/report', component: () => import('@/views/fault/FaultReport.vue'), meta: { title: '鏁呴殰涓婃姤', cacheName: 'FaultReport' } },
-      { path: 'fault/review', component: () => import('@/views/fault/XcbReview.vue'), meta: { title: 'rk_XCB鍔╃悊瀹℃牳', cacheName: 'XcbReview' } },
-      { path: 'fault/approval/cmb', component: () => import('@/views/fault/SimpleApproval.vue'), props: { stage: 'CMB' }, meta: { title: 'rk_CMB棰嗗瀹℃壒', stage: 'CMB', cacheName: 'SimpleApproval' } },
-      { path: 'fault/approval/jcc-assistant', component: () => import('@/views/fault/SimpleApproval.vue'), props: { stage: 'JCC_ASSISTANT' }, meta: { title: 'rk_JCC鍔╃悊瀹℃壒', stage: 'JCC_ASSISTANT', cacheName: 'SimpleApproval' } },
-      { path: 'fault/approval/jcc-director', component: () => import('@/views/fault/SimpleApproval.vue'), props: { stage: 'JCC_DIRECTOR' }, meta: { title: 'rk_JCC澶勯暱瀹℃壒', stage: 'JCC_DIRECTOR', cacheName: 'SimpleApproval' } },
-      { path: 'fault/dispatch', component: () => import('@/views/fault/DispatchPage.vue'), meta: { title: 'rk_XCB鍔╃悊娲惧崟', cacheName: 'DispatchPage' } },
+      { path: 'fault/review', component: () => import('@/views/fault/XcbReview.vue'), meta: { title: 'rk_绗竴瀹℃壒浜哄鏍�', cacheName: 'XcbReview' } },
+      { path: 'fault/approval/cmb', component: () => import('@/views/fault/SimpleApproval.vue'), props: { stage: 'CMB' }, meta: { title: 'rk_绗簩瀹℃壒浜哄鎵�', stage: 'CMB', cacheName: 'SimpleApproval' } },
+      { path: 'fault/approval/jcc-assistant', component: () => import('@/views/fault/SimpleApproval.vue'), props: { stage: 'JCC_ASSISTANT' }, meta: { title: 'rk_绗笁瀹℃壒浜哄鎵�', stage: 'JCC_ASSISTANT', cacheName: 'SimpleApproval' } },
+      { path: 'fault/approval/jcc-director', component: () => import('@/views/fault/SimpleApproval.vue'), props: { stage: 'JCC_DIRECTOR' }, meta: { title: 'rk_绗洓瀹℃壒浜哄鎵�', stage: 'JCC_DIRECTOR', cacheName: 'SimpleApproval' } },
+      { path: 'fault/dispatch', component: () => import('@/views/fault/DispatchPage.vue'), meta: { title: 'rk_绗竴瀹℃壒浜烘淳鍗�', cacheName: 'DispatchPage' } },
       { path: 'fault/completion', component: () => import('@/views/completion/CompletionList.vue'), meta: { title: '瀹屽伐鎯呭喌澶勭悊', cacheName: 'CompletionList' } },
+      { path: 'fault/engineering-print', component: () => import('@/views/fault/EngineeringPrint.vue'), meta: { title: '宸ョ▼鍗曟墦鍗�', cacheName: 'EngineeringPrint' } },
       { path: 'completion/report', component: () => import('@/views/completion/CompletionReport.vue'), props: route => ({ workOrderId: route.query.workOrderId || null }), meta: { title: '瀹屽伐淇℃伅涓婃姤', cacheName: 'CompletionReport' } },
       { path: 'completion/query', component: () => import('@/views/completion/CompletionQuery.vue'), meta: { title: '瀹屽伐淇℃伅鏌ラ槄', cacheName: 'CompletionQuery' } },
       { path: 'acceptance/application', component: () => import('@/views/acceptance/AcceptanceList.vue'), props: { stage: 'APPLICATION' }, meta: { title: '楠屾敹鐢宠', stage: 'APPLICATION', cacheName: 'AcceptanceList' } },
diff --git a/emergency-repair-web/src/utils/dict.js b/emergency-repair-web/src/utils/dict.js
index fa9f561..0e90727 100644
--- a/emergency-repair-web/src/utils/dict.js
+++ b/emergency-repair-web/src/utils/dict.js
@@ -2,10 +2,10 @@
   UNREPORTED: { text: '鏈笂鎶�', type: 'info' },
   REPORTED_XCB: { text: '宸蹭笂鎶k_XCB', type: 'warning' },
   RETURNED: { text: '涓婄骇鎵撳洖', type: 'danger' },
-  XCB_REVIEWED: { text: 'rk_XCB鍔╃悊瀹℃牳瀹屾垚', type: 'primary' },
-  CMB_APPROVED: { text: 'rk_CMB棰嗗瀹℃壒瀹屾垚', type: 'success' },
-  JCC_ASSISTANT_APPROVED: { text: 'rk_JCC鍔╃悊瀹℃壒瀹屾垚', type: 'success' },
-  JCC_DIRECTOR_APPROVED: { text: 'rk_JCC澶勯暱宸查槄', type: 'success' },
+  XCB_REVIEWED: { text: 'rk_绗竴瀹℃壒浜哄鏍稿畬鎴�', type: 'primary' },
+  CMB_APPROVED: { text: 'rk_绗簩瀹℃壒浜哄鎵瑰畬鎴�', type: 'success' },
+  JCC_ASSISTANT_APPROVED: { text: 'rk_绗笁瀹℃壒浜哄鎵瑰畬鎴�', type: 'success' },
+  JCC_DIRECTOR_APPROVED: { text: 'rk_绗洓瀹℃壒浜哄凡闃�', type: 'success' },
   DISPATCHED: { text: '宸叉淳鍗�', type: 'primary' },
   CANCELLED: { text: '宸查攢椤�', type: 'info' }
 }
diff --git a/emergency-repair-web/src/views/Dashboard.vue b/emergency-repair-web/src/views/Dashboard.vue
index 4b0c3f9..f33c881 100644
--- a/emergency-repair-web/src/views/Dashboard.vue
+++ b/emergency-repair-web/src/views/Dashboard.vue
@@ -28,7 +28,7 @@
         <div class="panel-head"><h3>甯哥敤鍔熻兘</h3></div>
         <div class="quick-list">
           <button @click="$router.push('/fault/report')"><i class="el-icon-edit-outline"></i><span>鏁呴殰涓婃姤</span></button>
-          <button @click="$router.push('/fault/review')"><i class="el-icon-s-check"></i><span>rk_XCB鍔╃悊瀹℃牳</span></button>
+          <button @click="$router.push('/fault/review')"><i class="el-icon-s-check"></i><span>rk_绗竴瀹℃壒浜哄鏍�</span></button>
           <button @click="$router.push('/fault/dispatch')"><i class="el-icon-s-promotion"></i><span>涓�閿淳鍗�</span></button>
           <button @click="$router.push('/fault/completion')"><i class="el-icon-finished"></i><span>瀹屽伐澶勭悊</span></button>
           <button @click="$router.push('/acceptance/application')"><i class="el-icon-document-checked"></i><span>楠屾敹鐢宠</span></button>
diff --git a/emergency-repair-web/src/views/fault/DispatchPage.vue b/emergency-repair-web/src/views/fault/DispatchPage.vue
index f9408f4..d5c7ba6 100644
--- a/emergency-repair-web/src/views/fault/DispatchPage.vue
+++ b/emergency-repair-web/src/views/fault/DispatchPage.vue
@@ -2,8 +2,8 @@
   <div class="page">
     <div class="page-title">
       <div>
-        <h2>rk_XCB鍔╃悊娲惧崟</h2>
-        <p>鏄剧ずrk_JCC澶勯暱瀹℃壒瀹屾垚涓旀湭娲惧崟鐨勬晠闅滐紝鎸夋壙淇崟浣嶄竴閿敓鎴愭淳宸ュ崟</p>
+        <h2>rk_绗竴瀹℃壒浜烘淳鍗�</h2>
+        <p>鏄剧ずrk_绗洓瀹℃壒浜哄鎵瑰畬鎴愪笖鏈淳鍗曠殑鏁呴殰锛屾寜鎵夸慨鍗曚綅涓�閿敓鎴愭淳宸ュ崟</p>
       </div>
       <el-button type="primary" icon="el-icon-s-promotion" :disabled="selection.length === 0" @click="dispatch">涓�閿淳鍗�</el-button>
     </div>
diff --git a/emergency-repair-web/src/views/fault/EngineeringPrint.vue b/emergency-repair-web/src/views/fault/EngineeringPrint.vue
new file mode 100644
index 0000000..f0bb246
--- /dev/null
+++ b/emergency-repair-web/src/views/fault/EngineeringPrint.vue
@@ -0,0 +1,272 @@
+<template>
+  <div class="page engineering-print-page">
+    <div class="page-title">
+      <div>
+        <h2>宸ョ▼鍗曟墦鍗�</h2>
+        <p>鎸夋煡璇㈡潯浠剁瓫閫夋晠闅滆褰曪紝鍕鹃�夊悗濂楃敤 Word 妯℃澘鐢熸垚鍥涜仈宸ョ▼鍗曪紝鏄庣粏鍙嚜鍔ㄥ椤�</p>
+      </div>
+    </div>
+
+    <div class="toolbar query-toolbar">
+      <span class="filter-label">rk_X鍙�</span>
+      <el-select v-model="query.xNo" placeholder="璇烽�夋嫨rk_X鍙�" style="width:110px">
+        <el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option>
+      </el-select>
+      <span class="filter-label">rk_T闃熶笓涓�</span>
+      <el-select v-model="query.teamMajor" placeholder="璇烽�夋嫨rk_T闃熶笓涓�" style="width:130px">
+        <el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option>
+      </el-select>
+      <span class="filter-label">宸ョ▼涓撲笟</span>
+      <el-select v-model="query.engineeringMajor" placeholder="鍏ㄩ儴" clearable style="width:130px">
+        <el-option v-for="item in engineeringMajors" :key="item.id" :label="item.name" :value="item.name"></el-option>
+      </el-select>
+      <span class="filter-label">涓婃姤鍗曚綅</span>
+      <el-input v-model="query.reportUnit" placeholder="鍗曚綅鍚嶇О" clearable style="width:150px" @keyup.enter.native="search"></el-input>
+      <span class="filter-label">璐熻矗浜�</span>
+      <el-input v-model="query.owner" placeholder="璐熻矗浜�" clearable style="width:110px" @keyup.enter.native="search"></el-input>
+      <span class="filter-label">涓婃姤鏃ユ湡</span>
+      <el-date-picker
+        v-model="query.reportDateRange"
+        type="daterange"
+        value-format="yyyy-MM-dd"
+        range-separator="鑷�"
+        start-placeholder="寮�濮嬫棩鏈�"
+        end-placeholder="缁撴潫鏃ユ湡"
+        style="width:300px"
+      ></el-date-picker>
+      <div class="filter-break"></div>
+      <span class="filter-label">浠诲姟鐘舵��</span>
+      <el-select v-model="query.status" placeholder="鍏ㄩ儴" clearable style="width:160px">
+        <el-option v-for="(meta, key) in taskStatuses" :key="key" :label="meta.text" :value="key"></el-option>
+      </el-select>
+      <el-input v-model="query.keyword" placeholder="鏁呴殰缂栧彿/璁惧鍚嶇О/宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�" clearable style="width:320px" @keyup.enter.native="search"></el-input>
+      <el-button type="primary" icon="el-icon-search" @click="search">鏌ヨ</el-button>
+      <el-button icon="el-icon-refresh-left" @click="resetQuery">閲嶇疆</el-button>
+      <el-button icon="el-icon-refresh" @click="load">鍒锋柊</el-button>
+    </div>
+
+    <div class="toolbar action-toolbar">
+      <span class="selection-tip">宸查�夋嫨 {{ selection.length }} 鏉�</span>
+      <div class="spacer"></div>
+      <el-button type="primary" icon="el-icon-printer" :loading="printing" :disabled="selection.length === 0" @click="print">鎵撳嵃宸ョ▼鍗�</el-button>
+      <el-button type="success" icon="el-icon-printer" :loading="printingV2" :disabled="selection.length === 0" @click="printV2">鎵撳嵃宸ョ▼鍗�2</el-button>
+    </div>
+
+    <div class="table-panel">
+      <el-table
+        ref="table"
+        :data="rows"
+        border
+        stripe
+        height="100%"
+        :header-cell-style="{ textAlign: 'center' }"
+        @selection-change="onSelectionChange"
+      >
+        <el-table-column type="selection" width="46"></el-table-column>
+        <el-table-column prop="taskStatus" label="浠诲姟鐘舵��" width="130" fixed>
+          <template slot-scope="{ row }"><el-tag size="mini" :type="taskMeta(row.taskStatus).type">{{ taskMeta(row.taskStatus).text }}</el-tag></template>
+        </el-table-column>
+        <el-table-column prop="faultCode" label="鏁呴殰缂栧彿" width="180" fixed></el-table-column>
+        <el-table-column prop="xNo" label="rk_X鍙�" width="70"></el-table-column>
+        <el-table-column prop="equipmentName" label="璁惧鍚嶇О" min-width="210" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="engineeringNameDesc" label="宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�" min-width="220" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="repairRequirement" label="淇悊鎶�鏈姹�" min-width="200" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="engineeringMajor" label="宸ョ▼涓撲笟" width="100"></el-table-column>
+        <el-table-column prop="teamMajor" label="rk_T闃熶笓涓�" width="100"></el-table-column>
+        <el-table-column prop="owner" label="璐熻矗浜�" width="90"></el-table-column>
+        <el-table-column prop="reportUnit" label="涓婃姤鍗曚綅" min-width="130"></el-table-column>
+        <el-table-column prop="reportDate" label="涓婃姤鏃ユ湡" width="110" align="center"></el-table-column>
+        <el-table-column prop="requestedFinishDate" label="瑕佹眰瀹屽伐鏃ユ湡" width="120" align="center"></el-table-column>
+      </el-table>
+    </div>
+
+    <div class="pagination-bar">
+      <el-pagination
+        background
+        :current-page="pagination.current"
+        :page-size="pagination.size"
+        :page-sizes="[20, 50, 100]"
+        :total="pagination.total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      ></el-pagination>
+    </div>
+  </div>
+</template>
+<script>
+import api from '@/api'
+import { taskStatuses, statusMeta } from '@/utils/dict'
+
+export default {
+  name: 'EngineeringPrint',
+  data() {
+    return {
+      taskStatuses,
+      query: { keyword: '', status: '', xNo: '', teamMajor: '', engineeringMajor: '', reportUnit: '', owner: '', reportDateRange: [] },
+      rows: [],
+      selection: [],
+      pagination: { current: 1, size: 20, total: 0 },
+      printing: false,
+      printingV2: false,
+      teamMajors: [],
+      engineeringMajors: [],
+      xNumbers: []
+    }
+  },
+  created() {
+    this.init()
+  },
+  methods: {
+    taskMeta(value) { return statusMeta(taskStatuses, value) },
+    async init() {
+      try {
+        await Promise.all([this.loadXNumbers(), this.loadMajors()])
+      } catch (e) {}
+      this.applyDefaultFilters()
+      this.load()
+    },
+    loadXNumbers() {
+      return api.get('/system/tree/product/x-numbers').then(res => { this.xNumbers = res.data || [] })
+    },
+    loadMajors() {
+      const team = api.get('/system/team-majors').then(res => { this.teamMajors = res.data || [] })
+      const engineering = api.get('/system/engineering-majors').then(res => { this.engineeringMajors = res.data || [] })
+      return Promise.all([team, engineering])
+    },
+    applyDefaultFilters() {
+      if (this.xNumbers.length && !this.xNumbers.includes(this.query.xNo)) {
+        this.query.xNo = this.xNumbers[0]
+      }
+      const teamNames = this.teamMajors.map(item => item.name)
+      if (teamNames.length && !teamNames.includes(this.query.teamMajor)) {
+        this.query.teamMajor = teamNames[0]
+      }
+    },
+    queryParams() {
+      const range = this.query.reportDateRange || []
+      return {
+        keyword: this.query.keyword,
+        status: this.query.status,
+        xNo: this.query.xNo,
+        teamMajor: this.query.teamMajor,
+        engineeringMajor: this.query.engineeringMajor,
+        reportUnit: this.query.reportUnit,
+        owner: this.query.owner,
+        reportDateStart: range[0] || '',
+        reportDateEnd: range[1] || ''
+      }
+    },
+    load() {
+      const params = {
+        ...this.queryParams(),
+        current: this.pagination.current,
+        size: this.pagination.size
+      }
+      return api.get('/faults/page', { params }).then(res => {
+        const page = res.data || {}
+        this.rows = page.records || []
+        this.pagination.total = Number(page.total || 0)
+        if (!this.rows.length && this.pagination.current > 1 && this.pagination.total > 0) {
+          this.pagination.current -= 1
+          this.load()
+        }
+      })
+    },
+    search() {
+      this.pagination.current = 1
+      this.load()
+    },
+    resetQuery() {
+      this.query = { keyword: '', status: '', xNo: '', teamMajor: '', engineeringMajor: '', reportUnit: '', owner: '', reportDateRange: [] }
+      this.applyDefaultFilters()
+      this.search()
+    },
+    handleCurrentChange(current) {
+      this.pagination.current = current
+      this.load()
+    },
+    handleSizeChange(size) {
+      this.pagination.size = size
+      this.pagination.current = 1
+      this.load()
+    },
+    onSelectionChange(rows) {
+      this.selection = rows
+    },
+    print() {
+      this.printDocument('/faults/engineering-print', '宸ョ▼鍗�.docx', 'printing')
+    },
+    printV2() {
+      this.printDocument('/faults/engineering-print-v2', '宸ョ▼鍗�2.docx', 'printingV2')
+    },
+    printDocument(url, filename, loadingKey) {
+      const ids = this.selection.map(item => item.id)
+      if (!ids.length) return
+      this[loadingKey] = true
+      return api.post(url, { ids }, { responseType: 'blob' })
+        .then(res => this.saveDocument(res, filename))
+        .catch(() => {})
+        .finally(() => { this[loadingKey] = false })
+    },
+    async saveDocument(res, filename) {
+      const blob = res instanceof Blob ? res : new Blob([res])
+      if (blob.type && blob.type.indexOf('json') >= 0) {
+        const text = await blob.text()
+        let message = '鐢熸垚宸ョ▼鍗曞け璐�'
+        try {
+          message = JSON.parse(text).message || message
+        } catch (e) {}
+        this.$message.error(message)
+        return
+      }
+      const link = document.createElement('a')
+      link.href = URL.createObjectURL(blob)
+      link.download = filename || '宸ョ▼鍗�.docx'
+      link.click()
+      URL.revokeObjectURL(link.href)
+      this.$message.success((filename || '宸ョ▼鍗�').replace('.docx', '') + '宸茬敓鎴�')
+    }
+  }
+}
+</script>
+<style scoped>
+.engineering-print-page {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  min-height: 0;
+  overflow: hidden;
+}
+.engineering-print-page .page-title,
+.engineering-print-page .toolbar,
+.engineering-print-page .pagination-bar {
+  flex: 0 0 auto;
+}
+.engineering-print-page .table-panel {
+  flex: 1 1 auto;
+  min-height: 0;
+}
+.engineering-print-page ::v-deep .el-table__body-wrapper,
+.engineering-print-page ::v-deep .el-table__fixed-body-wrapper {
+  scrollbar-width: none;
+  -ms-overflow-style: none;
+}
+.engineering-print-page ::v-deep .el-table__body-wrapper::-webkit-scrollbar,
+.engineering-print-page ::v-deep .el-table__fixed-body-wrapper::-webkit-scrollbar {
+  display: none;
+  width: 0;
+  height: 0;
+}
+.query-toolbar { margin-bottom: 8px; }
+.filter-break { flex-basis: 100%; height: 0; }
+.query-toolbar ::v-deep .el-range-separator { flex: 0 0 18px; min-width: 18px; }
+.action-toolbar { min-height: 50px; }
+.filter-label { margin-left: 4px; color: #4d6073; font-size: 13px; white-space: nowrap; }
+.selection-tip { color: #6d7f91; font-size: 13px; }
+.selection-hint { margin-left: 12px; color: #9aa7b4; font-size: 12px; }
+.pagination-bar { display: flex; justify-content: flex-end; padding: 12px 0 4px; }
+@media (max-width: 900px) {
+  .query-toolbar .el-date-editor { width: 100% !important; }
+}
+</style>
diff --git a/emergency-repair-web/src/views/fault/SimpleApproval.vue b/emergency-repair-web/src/views/fault/SimpleApproval.vue
index 89edea9..6558a0c 100644
--- a/emergency-repair-web/src/views/fault/SimpleApproval.vue
+++ b/emergency-repair-web/src/views/fault/SimpleApproval.vue
@@ -66,7 +66,7 @@
         <el-form-item label="瀹℃壒鎰忚">
           <el-input v-model="form.opinion" type="textarea" :rows="3" placeholder="璇峰~鍐欏鎵规剰瑙�"></el-input>
         </el-form-item>
-        <el-alert v-if="form.result === 'REJECT'" :closable="false" type="warning" show-icon title="瀹℃壒涓嶉�氳繃鏃剁洿鎺ラ��鍥瀝k_XCB鍔╃悊锛屼换鍔$姸鎬佸彉涓衡�滀笂绾ф墦鍥炩�濄��"></el-alert>
+        <el-alert v-if="form.result === 'REJECT'" :closable="false" type="warning" show-icon title="瀹℃壒涓嶉�氳繃鏃剁洿鎺ラ��鍥瀝k_绗竴瀹℃壒浜猴紝浠诲姟鐘舵�佸彉涓衡�滀笂绾ф墦鍥炩�濄��"></el-alert>
       </el-form>
       <template slot="footer">
         <el-button @click="dialogVisible = false">鍙栨秷</el-button>
@@ -89,13 +89,13 @@
   },
   computed: {
     pageTitle() {
-      return { CMB: 'rk_CMB棰嗗瀹℃壒', JCC_ASSISTANT: 'rk_JCC鍔╃悊瀹℃壒', JCC_DIRECTOR: 'rk_JCC澶勯暱瀹℃壒' }[this.stage]
+      return { CMB: 'rk_绗簩瀹℃壒浜哄鎵�', JCC_ASSISTANT: 'rk_绗笁瀹℃壒浜哄鎵�', JCC_DIRECTOR: 'rk_绗洓瀹℃壒浜哄鎵�' }[this.stage]
     },
     subtitle() {
       return {
-        CMB: '瀵箁k_XCB鍔╃悊瀹℃牳瀹屾垚鐨勬晠闅滆繘琛屾壒閲忛�氳繃鎴栭��鍥�',
-        JCC_ASSISTANT: '瀵箁k_CMB棰嗗瀹℃壒瀹屾垚鐨勬晠闅滆繘琛屾壒閲忛�氳繃鎴栭��鍥�',
-        JCC_DIRECTOR: '瀵箁k_JCC鍔╃悊瀹℃壒瀹屾垚鐨勬晠闅滆繘琛屾渶缁堝鎵癸紝閫氳繃鍚庤繘鍏ユ淳鍗�'
+        CMB: '瀵箁k_绗竴瀹℃壒浜哄鏍稿畬鎴愮殑鏁呴殰杩涜鎵归噺閫氳繃鎴栭��鍥�',
+        JCC_ASSISTANT: '瀵箁k_绗簩瀹℃壒浜哄鎵瑰畬鎴愮殑鏁呴殰杩涜鎵归噺閫氳繃鎴栭��鍥�',
+        JCC_DIRECTOR: '瀵箁k_绗笁瀹℃壒浜哄鎵瑰畬鎴愮殑鏁呴殰杩涜鏈�缁堝鎵癸紝閫氳繃鍚庤繘鍏ユ淳鍗�'
       }[this.stage]
     },
     status() {
diff --git a/emergency-repair-web/src/views/fault/XcbReview.vue b/emergency-repair-web/src/views/fault/XcbReview.vue
index 9210a9c..466534e 100644
--- a/emergency-repair-web/src/views/fault/XcbReview.vue
+++ b/emergency-repair-web/src/views/fault/XcbReview.vue
@@ -2,8 +2,8 @@
   <div class="page">
     <div class="page-title">
       <div>
-        <h2>rk_XCB鍔╃悊瀹℃牳</h2>
-        <p>鍒嗛厤鎵夸慨鍗曚綅銆佽窡韪妧鏈槦鍜岃姹傚畬宸ユ棩鏈燂紝瀹℃牳鍚庤繘鍏k_CMB棰嗗瀹℃壒</p>
+        <h2>rk_绗竴瀹℃壒浜哄鏍�</h2>
+        <p>鍒嗛厤鎵夸慨鍗曚綅銆佽窡韪妧鏈槦鍜岃姹傚畬宸ユ棩鏈燂紝瀹℃牳鍚庤繘鍏k_绗簩瀹℃壒浜哄鎵�</p>
       </div>
     </div>
 
@@ -97,7 +97,7 @@
 
     <fault-edit-dialog :visible.sync="editVisible" :fault="editingFault" @saved="onFaultSaved"></fault-edit-dialog>
 
-    <el-dialog title="rk_XCB鍔╃悊瀹℃牳" :visible.sync="dialogVisible" width="820px" append-to-body>
+    <el-dialog title="rk_绗竴瀹℃壒浜哄鏍�" :visible.sync="dialogVisible" width="820px" append-to-body>
       <el-alert :closable="false" type="info" show-icon title="鎵夸慨鍗曚綅鍙閫夈�傛帓搴忓墠7涓綔涓哄父鐢ㄦ壙淇崟浣嶄紭鍏堟樉绀猴紝澶变俊鍗曚綅鏍囪鎻愰啋銆�" style="margin-bottom:14px"></el-alert>
       <el-form :model="form" label-width="105px">
         <el-form-item label="宸查�夋晠闅�">
@@ -191,7 +191,7 @@
       cancelReason: '',
       cancelling: false,
       saving: false,
-      trackingTeams: ['T01闃�', 'T02闃�', 'T03闃�'],
+      trackingTeams: ['鎶�鏈槦1', '鎶�鏈槦2', '鎶�鏈槦3'],
       form: { contractorIds: [], trackingTeam: 'T01闃�', requestedFinishDate: '', opinion: '' }
     }
   },
@@ -292,7 +292,7 @@
       api.post('/faults/cancel', {
         ids: this.selection.map(item => item.id),
         reason: this.cancelReason.trim(),
-        cancelledBy: sessionStorage.getItem('realName') || 'rk_XCB鍔╃悊'
+        cancelledBy: sessionStorage.getItem('realName') || 'rk_绗竴瀹℃壒浜�'
       }).then(() => {
         this.$message.success('閿�椤瑰畬鎴�')
         this.cancelVisible = false
diff --git a/emergency-repair-web/vue.config.js b/emergency-repair-web/vue.config.js
index 6fdf28f..c172211 100644
--- a/emergency-repair-web/vue.config.js
+++ b/emergency-repair-web/vue.config.js
@@ -1,11 +1,26 @@
+// 鍓嶇绔彛銆佸悗绔湴鍧�缁熶竴鍦ㄩ」鐩牴鐩綍 .env 涓厤缃�
+const devPort = Number(process.env.PORT)
+const apiTarget = process.env.VUE_APP_API_TARGET
+const apiBase = process.env.VUE_APP_API_BASE_URL
+
+if (!devPort) {
+  throw new Error('缂哄皯鍓嶇绔彛閰嶇疆锛氳鍦� emergency-repair-web/.env 涓缃� PORT')
+}
+if (!apiTarget) {
+  throw new Error('缂哄皯鍚庣鍦板潃閰嶇疆锛氳鍦� emergency-repair-web/.env 涓缃� VUE_APP_API_TARGET')
+}
+if (!apiBase) {
+  throw new Error('缂哄皯鎺ュ彛鍩虹璺緞閰嶇疆锛氳鍦� emergency-repair-web/.env 涓缃� VUE_APP_API_BASE_URL')
+}
+
 module.exports = {
   parallel: false,
   devServer: {
-    port: 8082,
+    port: devPort,
     headers: { 'Cache-Control': 'no-store' },
     proxy: {
       '/api': {
-        target: 'http://127.0.0.1:8091',
+        target: apiTarget,
         changeOrigin: true
       }
     }
diff --git "a/requirement/\344\272\244\346\216\245\346\226\207\344\273\266_2026.10.3.md" "b/requirement/\344\272\244\346\216\245\346\226\207\344\273\266_2026.10.3.md"
index 2763f18..f1dc6d4 100644
--- "a/requirement/\344\272\244\346\216\245\346\226\207\344\273\266_2026.10.3.md"
+++ "b/requirement/\344\272\244\346\216\245\346\226\207\344\273\266_2026.10.3.md"
@@ -796,7 +796,7 @@
 
 | 闂 | 鐘舵�� | 鏍瑰洜涓庝慨澶� |
 | --- | --- | --- |
-| `rk_CMB棰嗗瀹℃壒` 閫氳繃鍚庤繘鍏� `rk_JCC鍔╃悊瀹℃壒`锛屽垪琛ㄦ病鏈夋暟鎹� | 宸蹭慨澶� | CMB銆丣CC 鍔╃悊銆丣CC 澶勯暱鍏辩敤 `SimpleApproval.vue` 鍜屽悓涓� keep-alive 缂撳瓨鍚嶏紱鍘熺粍浠朵粎鍦� `created()` 鏌ヨ涓�娆★紝鍒囨崲 `stage` 鏃舵病鏈夐噸鏂拌姹傘�傜幇鐩戝惉 `stage`锛屽垏鎹㈡椂娓呯┖鏃у垪琛ㄥ苟绔嬪嵆鎸夋柊鐘舵�佹煡璇紱缂撳瓨椤电鍐嶆婵�娲绘椂涔熶細鍒锋柊 |
+| `rk_绗簩瀹℃壒浜哄鎵筦 閫氳繃鍚庤繘鍏� `rk_绗笁瀹℃壒浜哄鎵筦锛屽垪琛ㄦ病鏈夋暟鎹� | 宸蹭慨澶� | CMB銆丣CC 鍔╃悊銆丣CC 澶勯暱鍏辩敤 `SimpleApproval.vue` 鍜屽悓涓� keep-alive 缂撳瓨鍚嶏紱鍘熺粍浠朵粎鍦� `created()` 鏌ヨ涓�娆★紝鍒囨崲 `stage` 鏃舵病鏈夐噸鏂拌姹傘�傜幇鐩戝惉 `stage`锛屽垏鎹㈡椂娓呯┖鏃у垪琛ㄥ苟绔嬪嵆鎸夋柊鐘舵�佹煡璇紱缂撳瓨椤电鍐嶆婵�娲绘椂涔熶細鍒锋柊 |
 
 ### 21.1 涓昏鍙樻洿鏂囦欢
 
diff --git "a/requirement/\345\206\263\347\255\226\350\241\250_\345\276\205\347\241\256\350\256\244\351\241\271.md" "b/requirement/\345\206\263\347\255\226\350\241\250_\345\276\205\347\241\256\350\256\244\351\241\271.md"
index eadb725..e8847e3 100644
--- "a/requirement/\345\206\263\347\255\226\350\241\250_\345\276\205\347\241\256\350\256\244\351\241\271.md"
+++ "b/requirement/\345\206\263\347\255\226\350\241\250_\345\276\205\347\241\256\350\256\244\351\241\271.md"
@@ -23,7 +23,7 @@
 >
 > **2026-09-16 涓婄骇鎵撳洖鍙e緞**锛氫笉閫愮骇鎵撳洖锛汣MB棰嗗銆丣CC鍔╃悊銆丣CC澶勯暱瀹℃壒涓嶉�氳繃鏃跺潎鐩存帴閫�鍥瀀CB鍔╃悊锛屼换鍔$姸鎬佷负鈥滀笂绾ф墦鍥炩�濓紱璇ョ姸鎬佷笌鈥滃凡涓婃姤XCB鈥濅笟鍔¤涔夌瓑浠凤紝浣嗕綔涓轰笉鍚岀姸鎬佸�间繚瀛橈紝浠ュ尯鍒嗘祦杞建杩广�俋CB鍔╃悊鏈幆鑺傚鏍镐笉閫氳繃鏃朵笉杞�滀笂绾ф墦鍥炩�濓細鍐呭涓嶅悎鏍肩敤鈥滀慨鏀光�濆鐞嗭紝涓嶄綔涓烘晠闅滃鐞嗘椂鐐光�滈攢椤光�濄��
 >
-> **2026-09-14 鐢ㄦ埛琛ュ厖纭**锛氣憼 鑹囬暱鐜妭涓嶇撼鍏ョ涓�鐗堬紱鈶� 鎶ヤ环澶勭悊绉诲嚭涓绘祦绋嬨�佲�滄姤浠风鐞嗏�濊彍鍗曚笌妯″潡涓嶅仛锛涒憿 JCC 鍔╃悊瀹℃壒涓哄繀缁忕幆鑺傦紱鈶� 绂荤嚎/鏁版嵁鍖呯浉鍏冲姛鑳芥暣浣撳彇娑堛�佸搴旇彍鍗曢」涓�骞跺垹闄わ紙C-12 宸茶В鍐筹級锛涒懁 AXU2 鏁版嵁鍖呭鍏ユ棤闇�鏍稿锛涒懃 C-15 鍘熷彛寰勪负缁熶竴鍐欌�淭鍛橀獙鏀垛�濓紱2026-09-20 楠屾敹娴佺▼鏀逛负5鑿滃崟鍚庯紝璇ュ悕绉颁笉鍐嶄綔涓鸿彍鍗曞悕锛屸�淭鍛樷�濊鑹蹭唬绉颁粛淇濈暀銆�
+> **2026-09-14 鐢ㄦ埛琛ュ厖纭**锛氣憼 T闀跨幆鑺備笉绾冲叆绗竴鐗堬紱鈶� 鎶ヤ环澶勭悊绉诲嚭涓绘祦绋嬨�佲�滄姤浠风鐞嗏�濊彍鍗曚笌妯″潡涓嶅仛锛涒憿 JCC 鍔╃悊瀹℃壒涓哄繀缁忕幆鑺傦紱鈶� 绂荤嚎/鏁版嵁鍖呯浉鍏冲姛鑳芥暣浣撳彇娑堛�佸搴旇彍鍗曢」涓�骞跺垹闄わ紙C-12 宸茶В鍐筹級锛涒懁 AXU2 鏁版嵁鍖呭鍏ユ棤闇�鏍稿锛涒懃 C-15 鍘熷彛寰勪负缁熶竴鍐欌�淭鍛橀獙鏀垛�濓紱2026-09-20 楠屾敹娴佺▼鏀逛负5鑿滃崟鍚庯紝璇ュ悕绉颁笉鍐嶄綔涓鸿彍鍗曞悕锛屸�淭鍛樷�濊鑹蹭唬绉颁粛淇濈暀銆�
 
 ## A. 鐣岄潰杩樺師閫愬崱鏍稿锛堝鐓� requirement\鐣岄潰杩樺師.html锛汚XU15 宸蹭簬 2026-08-26 鏍稿瀹屾垚锛�
 
@@ -67,7 +67,7 @@
 5. 銆愬凡瑙e喅 2026-09-20銆戔�滈敭娲惧崟鈥濈‘璁ゅ簲涓衡�滀竴閿淳鍗曗�濓紱淇敼銆佺敓鎴愪复淇伐绋嬪崟銆佸垹闄や簹澶嶏紙鐤戜负娓呴櫎閲嶅锛夌瓑鍘熸埅鍥句笟鍔℃寜閽殏涓嶉渶瑕併��
 6. 鏃犳埅鍥炬ā鍧楅〉闈㈢粨鏋勶細鐩戞祴淇℃伅銆佹煡璇紙宸ョ▼鍗�/宸ョ▼椤圭洰/鏁呴殰淇℃伅/閿�椤癸級銆佹姤浠风鐞嗭紙瀵煎嚭寰呮姤浠�/淇埞鍔炲垵瀹�/JCC瀹℃牳锛夈�佸熀纭�淇℃伅锛圝C淇℃伅/鐢ㄦ埛鏁版嵁鑼冨洿锛夆�斺�旀槸鍚︽湁鍙傝�冨師鍨�/绾歌川绋匡紝鎴栨巿鏉冭嚜琛岃璁★紱绯荤粺绠$悊鑿滃崟宸茬‘璁ゅ寘鍚壙淇崟浣嶇鐞嗐�佷骇鍝佺粨鏋勬爲绠$悊銆佽澶囧熀纭�搴撶鐞嗭紱缁熻鎶ヨ〃鑿滃崟宸茬‘璁�6椤瑰姛鑳斤紱瀹屽伐楠屾敹5涓彍鍗曞凡纭锛屼絾瀛楁涓庨〉闈㈢粏鑺傚緟璁捐銆�
 7. 銆愬凡瑙e喅銆戔�淭D鈥濈殑鍚箟锛�2026-09-09锛夛細TD=鑹囬槦锛堟晱鎰熻瘝浠gО锛屽凡褰曞叆鏁忔劅璇嶅瓧鍏革級锛涢椤碘�淭D鈥濅负缁熻琛ㄨ〃澶村垪锛堚�淛C淇℃伅鈥濇寜閽�=鑸拌埞淇℃伅宸茬‘璁� 2026-08-26锛�
-8. 銆愬凡瑙e喅 2026-09-11銆戣墖闀跨幆鑺備笉绾冲叆绗竴鐗堬細绠�鍖栨祦绋嬩粠 T鍛樻晠闅滀笂鎶� 鐩存帴杩涘叆 XCB鍔╃悊瀹℃牳
+8. 銆愬凡瑙e喅 2026-09-11銆慣闀跨幆鑺備笉绾冲叆绗竴鐗堬細绠�鍖栨祦绋嬩粠 T鍛樻晠闅滀笂鎶� 鐩存帴杩涘叆 XCB鍔╃悊瀹℃牳
 9. 瀛楀吀鏁版嵁鍙栧�硷細X鍙封�滃綋鍓嶇姸鎬佲�濓紙绀轰緥鈥滃皬淇�濓紝鍏朵綑寰呯‘璁わ級锛涙晠闅滃奖鍝嶏紙绀轰緥鈥滀緵姘斺�濓級銆佹晠闅滄�ц川锛堢ず渚嬧�滈噸澶р�濓級宸茬‘璁ゆ寜鍙厤缃瓧鍏稿瓧娈靛鐞嗭紝鍏朵粬鍙栧�间粛寰呰ˉ锛�2026-09-16锛夈��
 10. 銆怉XU15 琛ュ厖銆戝瓧娈垫潵婧愪笌鍙敼鎬э細X鍙�/宸ョ▼缂栧彿/寮�宸ユ椂闂�/瀹屽伐鏃堕棿/鎵夸慨鍗曚綅绛夛紝鍝簺绯荤粺甯﹀嚭銆佸摢浜涙墜濉�佸彲鍚︿慨鏀�
 11. 銆愯祫鏂欏緟绱㈠彇銆戝櫒鏉愮郴缁熷鎺ユ枃妗o紙鍣ㄦ潗鐩綍琛ㄧ粨鏋勩�佸櫒鏉怚D銆佹帴鍙f柟寮忕瓑锛夆�斺�旂敤鎴峰凡鏈夋枃妗o紝鎺ㄨ繘瀵规帴鏃剁储鍙�
diff --git "a/requirement/\345\211\21544\351\241\265\351\234\200\346\261\202\346\242\263\347\220\206.md" "b/requirement/\345\211\21544\351\241\265\351\234\200\346\261\202\346\242\263\347\220\206.md"
index 49c0dc1..7285e8a 100644
--- "a/requirement/\345\211\21544\351\241\265\351\234\200\346\261\202\346\242\263\347\220\206.md"
+++ "b/requirement/\345\211\21544\351\241\265\351\234\200\346\261\202\346\242\263\347\220\206.md"
@@ -68,7 +68,7 @@
 
 涓庡師濮嬮渶姹傜殑宸紓锛�
 
-- 涓嶅啀鍖呭惈鈥滆墖闀挎壒閲忓鎵� / 鑹囬暱瀹℃壒鈥濈幆鑺傦紝娴佺▼浠� T鍛樻晠闅滀笂鎶ョ洿鎺ヨ繘鍏� XCB鍔╃悊瀹℃牳銆�
+- 涓嶅啀鍖呭惈鈥淭闀挎壒閲忓鎵� / T闀垮鎵光�濈幆鑺傦紝娴佺▼浠� T鍛樻晠闅滀笂鎶ョ洿鎺ヨ繘鍏� XCB鍔╃悊瀹℃牳銆�
 - 鍙栨秷涓�鍒囩绾胯兘鍔涳細鍗曟満鐗�/绂荤嚎濉啓鏁版嵁鍖呫�佸唴缃戝鍏ュ鍑烘暟鎹寘锛�2026-09-14 纭鏁翠綋鍙栨秷骞跺垹闄ゅ搴旇彍鍗曢」锛氭暟鎹寘瀵煎叆銆佸畬宸ヤ俊鎭鍏ャ�佸鍑哄緟瀹屽伐鏁版嵁鍖呫�佹姤浠风鐞嗭級銆�
 - 鍙栨秷鈥淴CB 鍔╃悊瀹屽伐澶勭悊/瀵煎嚭瀹屽伐椤圭洰鈥濈幆鑺傦紝鏀逛负鍘傚鐩存帴涓婃姤瀹屽伐淇℃伅銆�
 - 鎶ヤ环澶勭悊涓嶅啀浣滀负涓绘祦绋嬬幆鑺傦紙2026-09-14 纭锛氣�滄姤浠风鐞嗏�濊彍鍗曚笌妯″潡鏁翠綋涓嶅仛锛夈��
@@ -106,8 +106,8 @@
 - 銆愬緟纭銆戦獙鏀舵祦绋嬪悇姝ラ鐨勬搷浣滆鑹层�佸畬宸ラ獙鏀舵墦鍗拌緭鍑虹殑瀹為檯鎵撳嵃琛屼负锛屼互鍙娾�滀慨鍚庤川閲忚瘎绾р�濅笌 AXU14鈥淭鍛樿瘎鍒嗏�濆瓧娈电殑鍏崇郴銆�
 - 鍘熷鎴浘娴佺▼锛堝瓨妗o紝宸蹭笉閲囩敤锛夛細鏁呴殰涓婃姤鏁版嵁鍖� 鈫� XCB鍔╃悊瀹℃牳锛堝彲鐩存帴閿�椤癸級鈫� CMB棰嗗瀹℃壒锛堝彲鎵撳洖涓婁竴姝ワ級鈫� JCC澶勯暱瀹℃壒 鈫� XCB鍔╃悊娲惧崟锛堟墦鍗版淳宸ュ崟锛夆啋 鍘傚绾夸笅淇悊 鈫� XCB鍔╃悊瀹屽伐澶勭悊锛堝鍑哄畬宸ラ」鐩�/鍗曟満鐗堝~鍐�/鍘傚绂荤嚎濉啓/鍐呯綉涓婃姤锛夆啋 鎵撳嵃瀹屽伐楠屾敹鍗曪紱T鍛橀獙鏀� 鈫� 鎶ヤ环澶勭悊锛堝鍑哄緟鎶ヤ环椤圭洰 鈫� 鍗曟満鐗堝~鍐�/瀵煎叆 鈫� 淇埞鍔炲姪鐞嗗浠� 鈫� JCC瀹′环锛夈��
 - 娴佺▼鍥捐妭鐐瑰甫瓒呴摼鎺ワ紙鏂囨。鍐呭惈浜や簰琛細鐐瑰嚮鑺傜偣鍦ㄦ柊绐楀彛鎵撳紑瀵瑰簲鐘舵�侀〉锛�
-- 涓存姠淇祦绋嬬姸鎬侊紙鏉ヨ嚜鏂囨。鐩綍锛屽搴斿悗鏂囪缁嗛〉锛屽墠 44 椤垫湭灞曞紑锛屼笖鐢ㄦ埛宸茬‘璁ゆ殏涓嶅鐞� AXU19鈥�31锛夛細0 娴佺▼鐘舵�� 鈫� 1 鏁呴殰涓婃姤 鈫� 2.1 鑹囬暱鎵归噺瀹℃壒 / 2.2 鑹囬暱瀹℃壒 鈫� 3.1 鍔╃悊瀹℃壒-鍚屾剰缁翠慨 / 3.2 鍔╃悊瀹℃壒-鎻愪氦浼氳鐮旂┒ / 3.3 鍔╃悊瀹℃壒-鍔炵粨 鈫� 4.1 澶勯暱鎵归噺瀹℃壒 / 4.2 澶勯暱瀹℃壒 鈫� 5.1 缁翠慨 / 5.2 缁翠慨淇℃伅
-- 銆愬凡瑙e喅 2026-09-11銆戣墖闀跨幆鑺備笉绾冲叆绗竴鐗堬紙绠�鍖栨祦绋嬩笉鍚鐜妭锛�
+- 涓存姠淇祦绋嬬姸鎬侊紙鏉ヨ嚜鏂囨。鐩綍锛屽搴斿悗鏂囪缁嗛〉锛屽墠 44 椤垫湭灞曞紑锛屼笖鐢ㄦ埛宸茬‘璁ゆ殏涓嶅鐞� AXU19鈥�31锛夛細0 娴佺▼鐘舵�� 鈫� 1 鏁呴殰涓婃姤 鈫� 2.1 T闀挎壒閲忓鎵� / 2.2 T闀垮鎵� 鈫� 3.1 鍔╃悊瀹℃壒-鍚屾剰缁翠慨 / 3.2 鍔╃悊瀹℃壒-鎻愪氦浼氳鐮旂┒ / 3.3 鍔╃悊瀹℃壒-鍔炵粨 鈫� 4.1 澶勯暱鎵归噺瀹℃壒 / 4.2 澶勯暱瀹℃壒 鈫� 5.1 缁翠慨 / 5.2 缁翠慨淇℃伅
+- 銆愬凡瑙e喅 2026-09-11銆慣闀跨幆鑺備笉绾冲叆绗竴鐗堬紙绠�鍖栨祦绋嬩笉鍚鐜妭锛�
 
 ## 4. 涓婃姤瀹℃壒閾撅紙AXU2鈥揂XU7锛�
 
@@ -292,7 +292,7 @@
 8. 銆愭殏涓嶅鐞� 2026-09-20銆慉XU16 涓嶇撼鍏ュ綋鍓嶉渶姹�
 9. 鐩戞祴淇℃伅/鏌ヨ/鎶ヤ环/鍩虹淇℃伅锛堟棤鎴浘妯″潡锛夌殑椤甸潰缁撴瀯锛涘畬宸ラ獙鏀舵祦绋嬬殑鑿滃崟涓庢搷浣滃凡纭锛岄〉闈㈠瓧娈典粛寰呰璁�
 10. 棣栭〉缁熻鍗$墖瀹屾暣娓呭崟锛圝C淇℃伅=鑸拌埞淇℃伅宸茬‘璁� 2026-08-26锛汿D 鍚箟寰呯‘璁わ級
-11. 銆愬凡瑙e喅 2026-09-11銆戔�滆墖闀挎壒閲忓鎵�/鑹囬暱瀹℃壒鈥濈幆鑺傦細绗竴鐗堢畝鍖栨祦绋嬩笉鍚墖闀跨幆鑺傦紝浠� T鍛樻晠闅滀笂鎶ョ洿鎺ヨ繘鍏� XCB鍔╃悊瀹℃牳
+11. 銆愬凡瑙e喅 2026-09-11銆戔�淭闀挎壒閲忓鎵�/T闀垮鎵光�濈幆鑺傦細绗竴鐗堢畝鍖栨祦绋嬩笉鍚玊闀跨幆鑺傦紝浠� T鍛樻晠闅滀笂鎶ョ洿鎺ヨ繘鍏� XCB鍔╃悊瀹℃牳
 12. 瀛楀吀鏁版嵁鍙栧�硷細鏁呴殰褰卞搷锛堢ず渚嬧�滀緵姘斺�濓級銆佹晠闅滄�ц川锛堢ず渚嬧�滈噸澶р�濓級鍧囦负鍙厤缃瓧鍏稿瓧娈碉紝鍏朵粬鍊煎緟琛ワ紱X鍙封�滃綋鍓嶇姸鎬佲�濈瓑鍏朵粬瀛楀吀鍊间篃寰呰ˉ
 13. 銆怉XU15 琛ュ厖 2026-09-08銆戝瓧娈垫潵婧愪笌鍙敼鎬э細X鍙�/宸ョ▼缂栧彿/寮�宸ユ椂闂�/瀹屽伐鏃堕棿/鎵夸慨鍗曚綅绛夛紝鍝簺绯荤粺甯﹀嚭銆佸摢浜涙墜濉�佸彲鍚︿慨鏀癸紙鍘� HANDOFF 绗� 6 鑺傜 13 鏉★紝鏈鍚屾鍏ュ唽锛�
 14. 銆愬凡瑙e喅 2026-09-14銆戠绾�/鏁版嵁鍖呯浉鍏冲姛鑳芥暣浣撳彇娑堬紝瀵瑰簲鑿滃崟椤逛竴骞跺垹闄わ細AXU2 鏁版嵁鍖呭鍏ワ紙椤甸潰涓庤彍鍗曢」鍒犻櫎锛屾棤闇�鏍稿锛夈�佸畬宸ヤ俊鎭鍏ャ�佸伐绋嬪崟鎵撳嵃杈撳嚭鐨勨�滃鍑哄緟瀹屽伐鏁版嵁鍖呪�濄�佹姤浠风鐞嗙殑瀵煎嚭/瀵煎叆銆�
@@ -316,4 +316,4 @@
 ## 10. 鍏充簬鏂囨。 45 椤典箣鍚庣殑璇存槑
 
 - 45 椤典箣鍚庡ぇ閮ㄥ垎涓衡�滆鍒掍慨鐞嗏�濓紙AXU32鈥揂XU66锛変笌鈥滄敼鎹㈣鈥濓紙AXU67鈥揂XU76锛夌殑鍘熷瀷椤甸潰锛屼笌涓存姠淇」鐩棤鍏炽��
-- 鏂囨。绗� 55鈥�67 椤电殑鈥滀复鎶慨鈥濊缁嗘祦绋嬮〉闈紙AXU19鈥揂XU31锛�0娴佺▼鐘舵��/0娴佺▼琛ㄦ牸/0.1娴佺▼鍥�/1鏁呴殰涓婃姤/2.1鑹囬暱鎵归噺瀹℃壒/2.2鑹囬暱瀹℃壒/3.1鈥�3.3鍔╃悊瀹℃壒/4.1鈥�4.2澶勯暱瀹℃壒/5.1缁翠慨/5.2缁翠慨淇℃伅锛夌粡纭銆愭殏涓嶅鐞嗐�戙��
+- 鏂囨。绗� 55鈥�67 椤电殑鈥滀复鎶慨鈥濊缁嗘祦绋嬮〉闈紙AXU19鈥揂XU31锛�0娴佺▼鐘舵��/0娴佺▼琛ㄦ牸/0.1娴佺▼鍥�/1鏁呴殰涓婃姤/2.1T闀挎壒閲忓鎵�/2.2T闀垮鎵�/3.1鈥�3.3鍔╃悊瀹℃壒/4.1鈥�4.2澶勯暱瀹℃壒/5.1缁翠慨/5.2缁翠慨淇℃伅锛夌粡纭銆愭殏涓嶅鐞嗐�戙��
diff --git "a/requirement/\345\267\245\347\250\213\347\212\266\346\200\201\346\234\272\350\256\276\350\256\241.md" "b/requirement/\345\267\245\347\250\213\347\212\266\346\200\201\346\234\272\350\256\276\350\256\241.md"
index 057a958..9651aa8 100644
--- "a/requirement/\345\267\245\347\250\213\347\212\266\346\200\201\346\234\272\350\256\276\350\256\241.md"
+++ "b/requirement/\345\267\245\347\250\213\347\212\266\346\200\201\346\234\272\350\256\276\350\256\241.md"
@@ -1,8 +1,8 @@
 # 涓存姠淇蒋浠堵峰伐绋嬬姸鎬佹満璁捐锛堣崏妗堬級
 
 > 鏃ユ湡锛�2026-08-25 锝� 褰撳墠渚濇嵁锛欰XU0/AXU3/AXU4/AXU10/AXU14/AXU15 鍙婄敤鎴峰凡纭鐨勬淳鍗曘�佸畬宸ョ姸鎬佸拰楠屾敹娴佺▼锛涘伐绋嬬姸鎬佺粺涓�璁捐涓虹姸鎬佹満
-> 2026-09-11 鏇存柊锛氭寜鐢ㄦ埛纭鐨勨�滅畝鍖栨祦绋� + 鍙仛缃戠粶鐗堚�濆彛寰勪慨璁紙鏃犺墖闀跨幆鑺傘�佹棤绂荤嚎鏁版嵁鍖咃紱2026-09-16 淇涓猴細XCB鍔╃悊瀹℃牳璐熻矗鍒嗛厤鎵夸慨鍗曚綅銆佹媶鍗曘�佺敓鎴愬伐绋嬪崟鍙凤紱2026-09-20 杩涗竴姝ヤ慨姝d负娲惧崟椤垫寜鏁呴殰閫夋嫨銆佹寜鎵夸慨鍗曚綅鐢熸垚娲惧伐鍗曪級銆�
-> 2026-09-14 鏇存柊锛氱绾�/鏁版嵁鍖呯浉鍏冲姛鑳芥暣浣撳彇娑堝苟鍒犻櫎鑿滃崟椤癸紙鏁版嵁鍖呭鍏ャ�佸畬宸ヤ俊鎭鍏ャ�佸鍑哄緟瀹屽伐鏁版嵁鍖呫�佹姤浠风鐞嗘ā鍧楋級锛涜墖闀跨幆鑺傜‘璁や笉绾冲叆绗竴鐗堬紱JCC 鍔╃悊瀹℃壒涓哄繀缁忕幆鑺傘��
+> 2026-09-11 鏇存柊锛氭寜鐢ㄦ埛纭鐨勨�滅畝鍖栨祦绋� + 鍙仛缃戠粶鐗堚�濆彛寰勪慨璁紙鏃燭闀跨幆鑺傘�佹棤绂荤嚎鏁版嵁鍖咃紱2026-09-16 淇涓猴細XCB鍔╃悊瀹℃牳璐熻矗鍒嗛厤鎵夸慨鍗曚綅銆佹媶鍗曘�佺敓鎴愬伐绋嬪崟鍙凤紱2026-09-20 杩涗竴姝ヤ慨姝d负娲惧崟椤垫寜鏁呴殰閫夋嫨銆佹寜鎵夸慨鍗曚綅鐢熸垚娲惧伐鍗曪級銆�
+> 2026-09-14 鏇存柊锛氱绾�/鏁版嵁鍖呯浉鍏冲姛鑳芥暣浣撳彇娑堝苟鍒犻櫎鑿滃崟椤癸紙鏁版嵁鍖呭鍏ャ�佸畬宸ヤ俊鎭鍏ャ�佸鍑哄緟瀹屽伐鏁版嵁鍖呫�佹姤浠风鐞嗘ā鍧楋級锛汿闀跨幆鑺傜‘璁や笉绾冲叆绗竴鐗堬紱JCC 鍔╃悊瀹℃壒涓哄繀缁忕幆鑺傘��
 > 2026-09-16 鏇存柊锛氫换鍔$姸鎬佺粺涓�涓轰竷涓�硷細鈥滄湭涓婃姤 / 宸蹭笂鎶CB / 涓婄骇鎵撳洖 / XCB鍔╃悊瀹℃牳瀹屾垚 / CMB棰嗗瀹℃壒瀹屾垚 / JCC鍔╃悊瀹℃壒瀹屾垚 / JCC澶勯暱宸查槄鈥濓紱AXU4 鎵夸慨鍗曚綅鍒嗛厤銆佹媶鍗曘�佸伐绋嬪崟鍙风敓鎴愬潎鍦� XCB鍔╃悊瀹℃牳鐜妭瀹屾垚銆�
 > 2026-09-17 鏇存柊锛欰XU5/6/7 涓夊紶瀹℃壒椤电粺涓�淇濈暀鍒楄〃銆佸埛鏂般�佹壒閲忓鎵癸紝鍘绘帀淇敼銆丆MB宸查槄銆侀攢椤广�佹坊鍔犺繘搴﹀娉紱缁熶竴鈥滃鎵光�濈敤璇拰鈥淛CC澶勯暱瀹℃壒鈥濆悕绉帮紱鏁呴殰淇℃伅澧炲姞涓夌粍瀹℃壒瀛楁銆�
 > 2026-09-20 鑼冨洿鏀跺彛锛欰XU9銆丄XU11鈥揂XU13銆丄XU16 鏆備笉澶勭悊锛屼笉绾冲叆褰撳墠闇�姹傦紱AXU8 缁х画鏆傜紦銆�
@@ -17,7 +17,7 @@
 - 鐘舵�佸彉鍖栬褰曟搷浣滀汉銆佹搷浣滄椂闂淬�佹搷浣滄剰瑙侊紝褰㈡垚鍙拷婧殑娴佽浆鏃ュ織锛汚XU4銆丄XU5/6/7 鍙� AXU14 宸茬‘璁や笉鎻愪緵鈥滄坊鍔犺繘搴﹀娉ㄢ�濇寜閽��
 - AXU5/6/7 涓嶆彁渚涢�氱敤鈥滀慨鏀�/閿�椤光�濇搷浣滐紱瀹℃壒浜哄彧鎵ц鎵归噺瀹℃壒锛屽鎵规剰瑙併�佸鎵圭粨鏋滃拰瀹℃壒鏃堕棿闅忔晠闅滀俊鎭繚瀛樸��
 - 鏁呴殰涓婃姤闃舵鐨勯攢椤瑰姩浣滅粺涓�鐢� XCB鍔╃悊鎵ц锛汿闃熷拰鍏朵粬瀹℃壒瑙掕壊涓嶆彁渚涢攢椤规搷浣溿�傞攢椤圭悊鐢便�侀攢椤逛汉銆侀攢椤规椂闂撮殢鏁呴殰淇℃伅淇濆瓨锛涢攢椤逛汉鑷姩鍙栧綋鍓嶇櫥褰� XCB鍔╃悊锛岄攢椤规椂闂磋嚜鍔ㄥ彇绯荤粺褰撳墠鏃堕棿銆�
-- 瀹℃壒鐜妭椤哄簭鍥哄畾锛圱鍛樻晠闅滀笂鎶モ啋XCB鍔╃悊瀹℃牳鈫扖MB棰嗗瀹℃壒鈫扟CC鍔╃悊瀹℃壒鈫扟CC澶勯暱瀹℃壒锛夛紝涓嶉�氳繃娴佺▼寮曟搸瀹炰緥/浠诲姟琛ㄩ┍鍔ㄣ�傦紙2026-09-11 鐢ㄦ埛纭绠�鍖栨祦绋嬶紝涓嶅惈鑹囬暱鐜妭锛�
+- 瀹℃壒鐜妭椤哄簭鍥哄畾锛圱鍛樻晠闅滀笂鎶モ啋XCB鍔╃悊瀹℃牳鈫扖MB棰嗗瀹℃壒鈫扟CC鍔╃悊瀹℃壒鈫扟CC澶勯暱瀹℃壒锛夛紝涓嶉�氳繃娴佺▼寮曟搸瀹炰緥/浠诲姟琛ㄩ┍鍔ㄣ�傦紙2026-09-11 鐢ㄦ埛纭绠�鍖栨祦绋嬶紝涓嶅惈T闀跨幆鑺傦級
 
 ## 1. 璁捐鎬濊矾锛氫袱灞傜姸鎬�
 
diff --git "a/requirement/\346\224\271\350\277\23320261003.txt" "b/requirement/\346\224\271\350\277\2332026.10.txt"
similarity index 83%
rename from "requirement/\346\224\271\350\277\23320261003.txt"
rename to "requirement/\346\224\271\350\277\2332026.10.txt"
index c1368af..937dba0 100644
--- "a/requirement/\346\224\271\350\277\23320261003.txt"
+++ "b/requirement/\346\224\271\350\277\2332026.10.txt"
@@ -43,6 +43,17 @@
 28锛岀粍缁囨満鏋勶紙閮ㄩ棬鏍戣〃锛夛紝澧炲姞鎵╁睍灞炴�ц缃紝鐩墠涓昏鍖呮嫭璐熻矗rk_T
 29锛岀敤鎴风鐞嗭紝澧炲姞鎵╁睍灞炴�ц缃紝鐩墠涓昏鏈夊伐绋嬩笓涓氥�乺k_TD涓撲笟锛岃礋璐k_T锛涚敤鎴峰垪琛ㄥ睍寮�鏄剧ず宸ョ▼涓撲笟銆乺k_T闃熶笓涓氥�佽礋璐k_T涓夊垪锛屾渶鍚庢搷浣滃垪鎻愪緵鈥滅紪杈戝睘鎬р�濄��
 30銆佹柊澧炴晠闅滐紝榛樿鍊硷紝鏁呴殰涓婃姤鍗曚綅榛樿濉啓褰撳墠鐧诲綍浜虹殑鍗曚綅锛岃矗浠婚粯璁ゅ~鍐欏綋鍓嶇櫥褰曚汉锛宺k_X鍙烽粯璁ゅ~鍐欏綋鍓嶇櫥褰曚汉鐨勫崟浣嶇殑鎵╁紶灞炴�ц礋璐k_T锛屽伐绋嬩笓涓氥�乺k_TD涓撲笟鍒嗗埆涓哄綋鍓嶇敤鎴风殑鎵╁睍灞炴�у伐绋嬩笓涓氥�乺k_TD涓撲笟
+31銆佹坊鍔犲畬宸ヨ瘎鍒嗭紝閲囩敤5鏄熻瘎鍒嗗強淇悗璐ㄩ噺鎰忚鏍�
+
+32銆佸鍔犲伐绋嬪崟鎵撳嵃锛屾柊寮�涓�涓彍鍗曞姛鑳�
+	椤甸潰绫诲悓鏁呴殰涓婃姤锛岄偅浜涙煡璇㈡潯浠堕兘瑕侊紝浣唕k_X鍙枫�乺k_TD涓撲笟娌℃湁鍏ㄩ儴閫夐」锛岄粯璁ゆ殏鏃堕�夋嫨绗竴涓�
+	瑕佹眰鎸夋ā鏉胯緭鍑哄埌word锛屽仛鎴愭椿鐨勶紝{}鍙烽噷鏄瓧娈靛悕锛� 鐩墠濡傚浘鎵�绀猴紝鍦ㄤ竴涓獁ord閲屾墦鍗帮紝4鑱旓紙UnitNo锛夛紝鏈�澶氶��6鏉¤褰曟墦鍗颁竴椤碉紝缂栧彿瀛楁鏆傛椂涓嶇
+	4鑱旓紙UnitNo锛夐偅涓斁鍦ㄦ渶鍙宠竟鏂囧瓧杞悜鏄剧ず锛� 閭d釜ZB鍨嬪彿锛歿rk_X鍙风殑鐖惰妭鐐癸紙鍦ㄤ骇鍝佹暟閲岋級鍚嶇О} 婕忎簡
+	鍋氫釜閰嶇疆姣�1鑱旀湁涓悕绉帮紝 杈撳嚭鍒皐ord鍚庢樉绀� 濡傗�滅1鑱�   涓婃姤浜衡��  杩欐牱鐨勬牸寮�  锛堜笂鎶ヤ汉鏄负绗�1鑱旈厤缃殑锛夊彲鑳戒互鍚庤繕鏈夊埆鐨勫湴鏂归渶瑕侊紝浣嗘瘡鑱旈厤缃拰杩欎釜閰嶇疆鏄笉涓�鏍�
+	涓嶉檺鍒�6鏉¤褰曪紝鍙互澶氶〉杈撳嚭锛屾瘡椤垫樉绀洪〉涓嶉檺鍒舵潯鏁帮紝瑕佹眰璁板綍涓嶈兘璺ㄩ〉鏄剧ず锛岀涓�涓〃鏍煎拰绗�33涓〃涓垎鍒浐瀹氬湪姣忛〉鐨勯《閮ㄥ拰搴曢儴锛�
+
+33銆佸墠绔鍙c�佸墠绔闂悗绔痠p鍙婄鍙e仛鎴愰厤缃�
+34銆�
 
 
 
diff --git "a/requirement/\346\225\217\346\204\237\350\257\215\345\255\227\345\205\270.md" "b/requirement/\346\225\217\346\204\237\350\257\215\345\255\227\345\205\270.md"
index 00f8b40..bce499e 100644
--- "a/requirement/\346\225\217\346\204\237\350\257\215\345\255\227\345\205\270.md"
+++ "b/requirement/\346\225\217\346\204\237\350\257\215\345\255\227\345\205\270.md"
@@ -29,6 +29,6 @@
 
 - 鏂囨。涓墍鏈夋秹鍙婁互涓婂惈涔夊涓�寰嬩娇鐢�**浠gО鍒�**鍚嶇О锛堣瘝鏍逛唬绉帮紝缁勫悎璇嶇洿鎺ヤ互浠gО涔﹀啓锛夈��
 - 鏈缁熶竴锛�2026-09-09锛夛細鐣岄潰/鏂囨。涓�寰嬪啓浠gО锛堝 X鍙�=鑸峰彿锛夛紝涓嶄娇鐢ㄨ繎浼肩湡瀹炶〃杩帮紙濡傗�滆埌鍙封�濓級锛汿D=鑹囬槦 浣滀负鐙珛璇嶆潯缁存姢銆�
-- 鏈鏇存柊锛�2026-09-20锛夛細2026-09-14 鏇剧粺涓�鍐欌�淭鍛橀獙鏀垛�濓紱鐜板畬宸ラ獙鏀舵祦绋嬪浐瀹氫负5涓彍鍗曪紙楠屾敹鐢宠/璐ㄦ鍛橀獙鏀�/璐ㄦ璐熻矗浜洪獙鏀�/瀹屽伐楠屾敹鎵撳嵃杈撳嚭/淇悗璐ㄩ噺璇勭骇锛夛紝涓嶅啀浣跨敤鈥淭鍛橀獙鏀垛�濅綔涓鸿彍鍗曞悕銆俆鍛�=鑹囧憳锛岀敱 T 璇嶆牴娲剧敓锛涜瘎鍒嗕笌瑙掕壊鍚嶄粛缁熶竴鍐欌�淭鍛樷�濄��
+- 鏈鏇存柊锛�2026-09-20锛夛細2026-09-14 鏇剧粺涓�鍐欌�淭鍛橀獙鏀垛�濓紱鐜板畬宸ラ獙鏀舵祦绋嬪浐瀹氫负5涓彍鍗曪紙楠屾敹鐢宠/璐ㄦ鍛橀獙鏀�/璐ㄦ璐熻矗浜洪獙鏀�/瀹屽伐楠屾敹鎵撳嵃杈撳嚭/淇悗璐ㄩ噺璇勭骇锛夛紝涓嶅啀浣跨敤鈥淭鍛橀獙鏀垛�濅綔涓鸿彍鍗曞悕銆俆鍛�=T鍛橈紝鐢� T 璇嶆牴娲剧敓锛涜瘎鍒嗕笌瑙掕壊鍚嶄粛缁熶竴鍐欌�淭鍛樷�濄��
 - 鏂板鏁忔劅璇嶇敱鐢ㄦ埛鎻愪緵鍚庤拷鍔犲埌绗� 2 鑺傝〃鏍煎強 JSON 閰嶇疆銆�
 - 鐢熶骇鐜鏇挎崲閫昏緫锛氫粎鏇挎崲 `real_name` 涓� `codename` 涓嶅悓涓旀湭浣跨敤鍗犱綅绗︾殑璇嶆潯锛涘瓧鍏镐笉鍙敤鏃剁户缁緭鍑哄紑鍙戜唬绉般��
\ No newline at end of file
diff --git "a/requirement/\347\225\214\351\235\242\350\277\230\345\216\237.html" "b/requirement/\347\225\214\351\235\242\350\277\230\345\216\237.html"
index 57734ba..0d10a9c 100644
--- "a/requirement/\347\225\214\351\235\242\350\277\230\345\216\237.html"
+++ "b/requirement/\347\225\214\351\235\242\350\277\230\345\216\237.html"
@@ -18,7 +18,7 @@
 </style></head><body>
 <h1>涓存姠淇蒋浠� 路 鐣岄潰璇嗗埆杩樺師锛堝師濮嬮渶姹�.docx 鍓� 44 椤碉級</h1>
 <p style="font-size:13px;color:#555">鐢ㄩ�旓細涓庡師濮嬫枃妗i�愬紶鏍稿 OCR/璇嗗埆缁撴灉銆傞粍鑹查珮浜� = 寰呯‘璁�/OCR鐤戣銆傝〃鏍煎垪涓烘í鍚戞帓鍒楋紝涓嶄唬琛ㄥ師鍥惧疄闄呭垪椤哄簭銆�</p>
-<div class="note">鍏ㄥ眬鍙e緞锛�2026-09-11 鐢ㄦ埛纭锛夛細绗竴鐗�<mark>涓嶆寜鍘熷闇�姹�</mark>瀹炵幇锛屾敼涓� 鈶� <b>绠�鍖栦笟鍔℃祦绋�</b> + 鈶� <b>鍙彁渚涚綉缁滅増銆佷笉鎻愪緵绂荤嚎鍗曟満鐗�</b>锛涚畝鍖栨祦绋嬭 AXU1 鍗$墖銆傛墍鏈夆�滃崟鏈虹増濉啓/绂荤嚎濉啓/鏁版嵁鍖呭鍏ュ鍑衡�濈浉鍏冲姛鑳藉潎涓嶈繘鍏ョ涓�鐗堛��<mark>2026-09-20 鐢ㄦ埛纭锛欽CC澶勯暱瀹℃壒瀹屾垚鍚庣洿鎺ヨ繘鍏CB鍔╃悊娲惧崟椤甸潰锛涢〉闈㈡樉绀烘湭娲惧崟鏁呴殰锛屽嬀閫夊悗鐐瑰嚮鈥滀竴閿淳鍗曗�濓紱鎸夋壙淇崟浣嶇敓鎴愭淳宸ュ崟锛岀紪鍙蜂负鏁呴殰缂栧彿 + `/01`銆乣/02` 绛夈�傞櫎鈥滀竴閿淳鍗曗�濆锛屽師 AXU10 鍏朵粬涓氬姟鎸夐挳鏆備笉闇�瑕併�傛晠闅滅紪鍙风敱绯荤粺鎸� `鍥哄畾鍓嶇紑-X鍙�/鍒嗙被-YYMMDD-nnn` 鑷姩鐢熸垚锛屾晠闅滀俊鎭繚鐣欌�滄晠闅滅紪鍙封�濆瓧娈碉紙2026-09-28 鏇存柊锛夈��</mark><mark>2026-09-14 鐢ㄦ埛纭锛氱绾�/鏁版嵁鍖呯浉鍏冲姛鑳芥暣浣撳彇娑堛�佸搴旇彍鍗曢」涓�骞跺垹闄わ紙C-12 宸茶В鍐筹級锛涒�滄姤浠风鐞嗏�濊彍鍗曚笌妯″潡鏁翠綋涓嶅仛锛堟姤浠峰鐞嗙Щ鍑轰富娴佺▼锛夛紱鑹囬暱鐜妭涓嶇撼鍏ョ涓�鐗堬紱JCC 鍔╃悊瀹℃壒涓哄繀缁忕幆鑺傦紱AXU2 鏁版嵁鍖呭鍏ユ棤闇�鏍稿銆�</mark></div>
+<div class="note">鍏ㄥ眬鍙e緞锛�2026-09-11 鐢ㄦ埛纭锛夛細绗竴鐗�<mark>涓嶆寜鍘熷闇�姹�</mark>瀹炵幇锛屾敼涓� 鈶� <b>绠�鍖栦笟鍔℃祦绋�</b> + 鈶� <b>鍙彁渚涚綉缁滅増銆佷笉鎻愪緵绂荤嚎鍗曟満鐗�</b>锛涚畝鍖栨祦绋嬭 AXU1 鍗$墖銆傛墍鏈夆�滃崟鏈虹増濉啓/绂荤嚎濉啓/鏁版嵁鍖呭鍏ュ鍑衡�濈浉鍏冲姛鑳藉潎涓嶈繘鍏ョ涓�鐗堛��<mark>2026-09-20 鐢ㄦ埛纭锛欽CC澶勯暱瀹℃壒瀹屾垚鍚庣洿鎺ヨ繘鍏CB鍔╃悊娲惧崟椤甸潰锛涢〉闈㈡樉绀烘湭娲惧崟鏁呴殰锛屽嬀閫夊悗鐐瑰嚮鈥滀竴閿淳鍗曗�濓紱鎸夋壙淇崟浣嶇敓鎴愭淳宸ュ崟锛岀紪鍙蜂负鏁呴殰缂栧彿 + `/01`銆乣/02` 绛夈�傞櫎鈥滀竴閿淳鍗曗�濆锛屽師 AXU10 鍏朵粬涓氬姟鎸夐挳鏆備笉闇�瑕併�傛晠闅滅紪鍙风敱绯荤粺鎸� `鍥哄畾鍓嶇紑-X鍙�/鍒嗙被-YYMMDD-nnn` 鑷姩鐢熸垚锛屾晠闅滀俊鎭繚鐣欌�滄晠闅滅紪鍙封�濆瓧娈碉紙2026-09-28 鏇存柊锛夈��</mark><mark>2026-09-14 鐢ㄦ埛纭锛氱绾�/鏁版嵁鍖呯浉鍏冲姛鑳芥暣浣撳彇娑堛�佸搴旇彍鍗曢」涓�骞跺垹闄わ紙C-12 宸茶В鍐筹級锛涒�滄姤浠风鐞嗏�濊彍鍗曚笌妯″潡鏁翠綋涓嶅仛锛堟姤浠峰鐞嗙Щ鍑轰富娴佺▼锛夛紱T闀跨幆鑺備笉绾冲叆绗竴鐗堬紱JCC 鍔╃悊瀹℃壒涓哄繀缁忕幆鑺傦紱AXU2 鏁版嵁鍖呭鍏ユ棤闇�鏍稿銆�</mark></div>
 <div class="note"><b>鑼冨洿鏀跺彛锛�2026-09-20锛夛細</b><mark>AXU9銆丄XU11銆丄XU12銆丄XU13銆丄XU16 鏆備笉澶勭悊锛屼笉绾冲叆褰撳墠闇�姹備笌鍚庣画鏍稿锛汚XU8 缁х画鏆傜紦銆�</mark> 鍘熷鎴浘鏂囧瓧浠嶄繚鐣欙紝渚涜祫鏂欒拷婧��</div>
 <h2>AXUnn 鈫� 鑿滃崟鍚� 鈫� 鏂囨。椤� 瀵圭収琛�</h2>
 <table><tr><th>鎴浘缂栧彿</th><th>鑿滃崟鍚�/椤甸潰鍚�</th><th>鏂囨。椤�</th></tr><tr><td><b>AXU0</b></td><td>棣栭〉闂ㄦ埛</td><td>鏂囨。椤� 5</td></tr>
@@ -56,9 +56,9 @@
 <div class="label">绗竴鐗堢畝鍖栨祦绋嬶紙10 姝ワ紝宸茬‘璁� 2026-09-11锛�</div>
 <ol class="flow"><li>1. T鍛樻晠闅滀笂鎶�</li><li>2. XCB鍔╃悊瀹℃牳</li><li>3. CMB棰嗗瀹℃壒</li><li>4. JCC鍔╃悊瀹℃壒</li><li>5. JCC澶勯暱瀹℃壒</li><li>6. XCB鍔╃悊娲惧崟 鈥斺�� 鏄剧ず鏈淳鍗曟晠闅滐紝鍕鹃�夊悗鐐瑰嚮鈥滀竴閿淳鍗曗�濓紱鎸夋壙淇崟浣嶇敓鎴愭淳宸ュ崟锛屼笉鍐嶅垎閰嶆垨淇敼鎵夸慨鍗曚綅</li><li>7. 鍘傚绾夸笅淇悊锛堢郴缁熷绾夸笅鐜妭锛�</li><li>8. 鍘傚涓婃姤瀹屽伐淇℃伅</li><li>9. 瀹屽伐楠屾敹娴佺▼锛氶獙鏀剁敵璇� 鈫� 璐ㄦ鍛橀獙鏀� 鈫� 璐ㄦ璐熻矗浜洪獙鏀� 鈫� 瀹屽伐楠屾敹鎵撳嵃杈撳嚭 鈫� 淇悗璐ㄩ噺璇勭骇</li><li>10. 楠屾敹瀹屾垚</li></ol><div class="note2">閫�鍥炶鍒欙紙2026-09-16 纭锛夛細CMB棰嗗銆丣CC鍔╃悊銆丣CC澶勯暱瀹℃壒涓嶉�氳繃鏃讹紝鍧囩洿鎺ラ��鍥瀀CB鍔╃悊锛屼换鍔$姸鎬佷负鈥滀笂绾ф墦鍥炩�濓紱涓嶉�愮骇鎵撳洖銆傗�滀笂绾ф墦鍥炩�濅笌鈥滃凡涓婃姤XCB鈥濅笟鍔¤涔夌瓑浠凤紝浣嗕綔涓轰笉鍚岀姸鎬佸�间繚瀛橈紝浠ュ尯鍒嗘祦杞建杩广��</div>
 <div class="label">鐩稿鍘熷闇�姹傜殑宸紓锛�2026-09-11 纭锛�</div>
-<div class="note2">鈶� 涓嶅啀鍖呭惈鈥滆墖闀挎壒閲忓鎵�/鑹囬暱瀹℃壒鈥濓紝鐩存帴浠� T鍛樻晠闅滀笂鎶ュ紑濮嬶紱鈶� 鍙栨秷鈥滃崟鏈虹増濉啓/绂荤嚎濉啓鏁版嵁鍖� + 鍐呯綉瀵煎叆/瀵煎嚭鏁版嵁鍖呪�濓紱鈶� 鍙栨秷鈥淴CB鍔╃悊瀹屽伐澶勭悊锛堝鍑哄畬宸ラ」鐩�/鍗曟満鐗堝~鍐�/鍘傚绂荤嚎濉啓锛夆�濈幆鑺傦紝鏀逛负鍘傚鐩存帴涓婃姤瀹屽伐淇℃伅锛涒懀 鎶ヤ环澶勭悊涓嶅啀浣滀负涓绘祦绋嬬幆鑺傦紙鈥滄姤浠风鐞嗏�濊彍鍗曟槸鍚︿繚鐣欏緟纭锛夛紱鈶� JCC鍔╃悊瀹℃壒纭涓烘祦绋嬪繀缁忕幆鑺傦紙鍘熸埅鍥炬祦绋嬪浘鏈崟鍒楋級銆�</div>
+<div class="note2">鈶� 涓嶅啀鍖呭惈鈥淭闀挎壒閲忓鎵�/T闀垮鎵光�濓紝鐩存帴浠� T鍛樻晠闅滀笂鎶ュ紑濮嬶紱鈶� 鍙栨秷鈥滃崟鏈虹増濉啓/绂荤嚎濉啓鏁版嵁鍖� + 鍐呯綉瀵煎叆/瀵煎嚭鏁版嵁鍖呪�濓紱鈶� 鍙栨秷鈥淴CB鍔╃悊瀹屽伐澶勭悊锛堝鍑哄畬宸ラ」鐩�/鍗曟満鐗堝~鍐�/鍘傚绂荤嚎濉啓锛夆�濈幆鑺傦紝鏀逛负鍘傚鐩存帴涓婃姤瀹屽伐淇℃伅锛涒懀 鎶ヤ环澶勭悊涓嶅啀浣滀负涓绘祦绋嬬幆鑺傦紙鈥滄姤浠风鐞嗏�濊彍鍗曟槸鍚︿繚鐣欏緟纭锛夛紱鈶� JCC鍔╃悊瀹℃壒纭涓烘祦绋嬪繀缁忕幆鑺傦紙鍘熸埅鍥炬祦绋嬪浘鏈崟鍒楋級銆�</div>
 <div class="note2">鍘熷鎴浘娴佺▼锛堝瓨妗e弬鑰冿紝宸蹭笉閲囩敤锛夛細鏁呴殰涓婃姤鏁版嵁鍖� 鈫� XCB鍔╃悊瀹℃牳锛堝彲鐩存帴閿�椤癸級鈫� CMB棰嗗瀹℃壒锛堝彲鎵撳洖涓婁竴姝ワ級鈫� JCC澶勯暱瀹℃壒 鈫� XCB鍔╃悊娲惧崟锛堟墦鍗版淳宸ュ崟锛氬悓X鍙�+鍚屼笂鎶ユ棩鏈�+鍚屾壙淇崟浣嶅悎鎴愪竴涓淳宸ュ崟锛夆啋 鍘傚绾夸笅淇悊 鈫� XCB鍔╃悊瀹屽伐澶勭悊锛堝鍑哄畬宸ラ」鐩�/鍗曟満鐗堝~鍐�/鍘傚绂荤嚎濉啓/鍐呯綉涓婃姤锛夆啋 鎵撳嵃瀹屽伐楠屾敹鍗曪紱T鍛橀獙鏀� 鈫� 鎶ヤ环澶勭悊锛堝鍑哄緟鎶ヤ环椤圭洰 鈫� 鍗曟満鐗堝~鍐�/瀵煎叆 鈫� 淇埞鍔炲姪鐞嗗浠� 鈫� JCC瀹′环锛夈��</div>
-<div class="note2">鏈锛�2026-09-14 鐢ㄦ埛纭锛夛細缁熶竴鍐欌�淭鍛樷�濓紙T=鑹囦唬绉帮紝T鍛�=鑹囧憳锛夛紱鍘熸枃妗h彍鍗曠殑鏃у啓娉曞凡浣滃簾锛岃瘎鍒嗕笌瑙掕壊鍚嶄竴骞剁粺涓�涓衡�淭鍛樷�濄��</div>
+<div class="note2">鏈锛�2026-09-14 鐢ㄦ埛纭锛夛細缁熶竴鍐欌�淭鍛樷�濓紙T=鑹囦唬绉帮紝T鍛�=T鍛橈級锛涘師鏂囨。鑿滃崟鐨勬棫鍐欐硶宸蹭綔搴燂紝璇勫垎涓庤鑹插悕涓�骞剁粺涓�涓衡�淭鍛樷�濄��</div>
 </section><section class="card"><h3>AXU2 路 鏁版嵁鍖呭鍏ワ紙鏂囨。椤� 8锛�</h3><div class="note">瀵煎叆鏁呴殰涓婃姤鏁版嵁鍖呮枃浠�</div><div class="label">鎸夐挳/鎿嶄綔</div><div class="chips"><span class="td">閫夋嫨鏂囦欢</span><span class="td">瀵煎叆鏁版嵁鍖�</span></div><div class="note2">瀵煎叆缁撴灉缁熻</div><div class="label">鍒楄〃鍒楋紙琛ㄥご锛�</div><div class="chips"><span class="td">缁熻鍒嗙被</span><span class="td">鏁伴噺</span><span class="td">鏄庣粏</span></div><div class="note2">缁熻鍒嗙被鏄庣粏锛氶」鐩暟閲� / 娑夊強宸ョ▼涓撲笟 / 娑夊強T闃熶笓涓� / 娑夊強瑁呭</div><div class="note2">銆愬凡纭 2026-09-14銆戝彧鍋氱綉缁滅増銆佹棤瀵煎叆鍔熻兘 鈫� 鏈〉鏁翠綋鍙栨秷锛岃彍鍗曢」鈥滄暟鎹寘瀵煎叆鈥濆垹闄わ紝鏃犻渶鏍稿銆�</div></section><section class="card"><h3>AXU3 路 鏁呴殰涓婃姤锛堟枃妗i〉 9锛�</h3><div class="note">椤甸潰鏍囬锛氬垪琛�</div><div class="label">鏌ヨ鏉′欢</div><div class="chips"><span class="td">X鍙�</span><span class="td">T闃熶笓涓�</span><span class="td">宸ョ▼涓撲笟</span><span class="td">涓婃姤鍗曚綅</span><span class="td">璐熻矗浜�</span><span class="td">涓婃姤鏃ユ湡锛堣捣锛夆�旓紙鑷筹級</span></div><div class="label">鎸夐挳/鎿嶄綔</div><div class="chips"><span class="td">鏌ヨ</span><span class="td">鍒锋柊</span><span class="td">鏂板</span><span class="td">涓婃姤</span><span class="td">淇敼</span><span class="td">鍒犻櫎</span><span class="td">瀵煎嚭</span></div><div class="note2">銆愬凡纭 2026-09-16銆戝瓧娈典笌鎸夐挳閫氳鏃犺锛涒�滃~鍐欐晠闅滃垎鏋愭姤鍛娾�濈涓�鐗堟殏涓嶅疄鐜帮紝涓氬姟閫昏緫寰呭悗缁槑纭��</div><div class="label">鍒楄〃鍒楋紙琛ㄥご锛�</div><div class="chips"><span class="td">搴忓彿</span><span class="td">鏁呴殰缂栧彿</span><span class="td">浠诲姟鐘舵��</span><span class="td">涓婃姤鏃ユ湡</span><span class="td">涓婃姤鍗曚綅</span><span class="td">瑁呭鍚嶇О</span><span class="td">鎵�灞炰笂绾х郴缁�</span><span class="td">宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�</span><span class="td">淇悊鎶�鏈姹�</span><span class="td">宸ョ▼涓撲笟</span><span class="td">T闃熶笓涓�</span><span class="td">璺熻釜鎶�鏈槦</span><span class="td">璐熻矗浜�</span><span class="td">鏁呴殰褰卞搷</span><span class="td">鏁呴殰鎬ц川</span><span class="td">閲嶅ぇ鏁呴殰鎻忚堪</span><span class="td">鏄惁鑷慨</span><span class="td">鏄惁娑塇</span><span class="td">鐩戞祴椤圭洰</span><span class="td">鐩戞祴鏁板��</span><span class="td">瑕佹眰瀹屽伐鏃ユ湡</span><span class="td">澶囨敞</span></div><div class="note2">浠诲姟鐘舵�侊紙2026-09-16 纭锛屽叏灞�涓冧釜鍊硷級锛氭湭涓婃姤 / 宸蹭笂鎶CB / 涓婄骇鎵撳洖 / XCB鍔╃悊瀹℃牳瀹屾垚 / CMB棰嗗瀹℃壒瀹屾垚 / JCC鍔╃悊瀹℃壒瀹屾垚 / JCC澶勯暱宸查槄銆�</div><div class="note2">鍒楄〃琛ㄥご澧炲姞鈥滀笂鎶ユ棩鏈熲�濃�滀笂鎶ュ崟浣嶁�濓紙2026-09-16 纭锛夈��</div><div class="note2">鍒犻櫎闄愬埗锛�2026-09-16 纭锛夛細鏈笂鎶ョ殑鏁呴殰鍙互鍒犻櫎锛涘凡涓婃姤鐨勬晠闅滀笉鑳藉垹闄わ紱浠诲姟鐘舵�佷负鈥滀笂绾ф墦鍥炩�濇椂浠嶅厑璁稿垹闄ゃ��</div><div class="note2">瀹℃壒瀛楁缁勶紙2026-09-17 纭锛夛細CMB棰嗗瀹℃壒鎰忚銆丆MB棰嗗瀹℃壒缁撴灉锛堥�氳繃/涓嶉�氳繃锛夈�丆MB棰嗗瀹℃壒鏃堕棿锛汮CC鍔╃悊瀹℃壒鎰忚銆丣CC鍔╃悊瀹℃壒缁撴灉锛堥�氳繃/涓嶉�氳繃锛夈�丣CC鍔╃悊瀹℃壒鏃堕棿锛汮CC澶勯暱瀹℃壒鎰忚銆丣CC澶勯暱瀹℃壒缁撴灉锛堥�氳繃/涓嶉�氳繃锛夈�丣CC澶勯暱瀹℃壒鏃堕棿銆�</div><div class="note2">閿�椤瑰瓧娈电粍锛�2026-09-17 纭锛夛細閿�椤圭悊鐢便�侀攢椤逛汉銆侀攢椤规椂闂达紱鏁呴殰涓婃姤闃舵鐨勯攢椤圭粺涓�鐢盭CB鍔╃悊鎵ц锛孴闃熷拰鍏朵粬瀹℃壒瑙掕壊涓嶆彁渚涢攢椤规搷浣溿�傞攢椤逛汉鑷姩鍙栧綋鍓嶇櫥褰昘CB鍔╃悊锛岄攢椤规椂闂磋嚜鍔ㄥ彇绯荤粺褰撳墠鏃堕棿銆�</div><div class="label">鏂板/缂栬緫琛ㄥ崟瀛楁锛�2026-09-16 纭锛氭柊澧炴敼涓鸿〃鍗曞紡锛屽師闇�姹傝〃鏍煎紡宸茶皟鏁达級锛�</div><div class="chips"><span class="td">鏁呴殰缂栧彿锛堝彧璇伙紝鑷姩鐢熸垚锛�</span><span class="td">X鍙�</span><span class="td">涓婃姤鍗曚綅</span><span class="td">T闃熶笓涓�</span><span class="td">璺熻釜鎶�鏈槦</span><span class="td">璐熻矗浜�</span><span class="td">涓婃姤鏃ユ湡</span><span class="td">宸ョ▼涓撲笟</span><span class="td">瑁呭鍚嶇О</span><span class="td">鍒嗘満/閮ㄤ綅</span><span class="td">鎵�灞炰笂绾х郴缁�</span><span class="td">宸ョ▼鍚嶇О</span><span class="td">淇悊鎶�鏈姹�</span><span class="td">鏁呴殰褰卞搷<mark>鍙厤缃瓧鍏革紱绀轰緥鍊硷細渚涙皵锛涘叾浠栧�煎緟纭</mark></span><span class="td">鏁呴殰鎬ц川<mark>鍙厤缃瓧鍏革紱绀轰緥鍊硷細閲嶅ぇ锛涘叾浠栧�煎緟纭</mark></span><span class="td">閲嶅ぇ鏁呴殰鎻忚堪</span><span class="td">鏄惁鑷慨</span><span class="td">鏄惁娑塇</span><span class="td">鐩戞祴椤圭洰</span><span class="td">鐩戞祴鏁板��</span><span class="td">瑕佹眰瀹屽伐鏃ユ湡</span><span class="td">澶囨敞</span></div><div class="note2">瀛楁璋冩暣锛�2026-09-16 纭锛夛細鍋滄硦鍦板瓧娈典笉淇濈暀銆�</div><div class="note2">鏁呴殰缂栧彿锛�2026-09-28 纭锛屾浛浠� 2026-09-20 鍙e緞锛夛細鐢辩郴缁熻嚜鍔ㄧ敓鎴愶紝瑙勫垯 <b>鍥哄畾鍓嶇紑-X鍙�/鍒嗙被-YYMMDD-nnn</b>锛沊鍙峰彇3浣嶆暟瀛楋紝鍒嗙被鍙栤�淭闃熶笓涓氶厤缃�濈殑 A/B锛孻YMMDD 涓轰笂鎶ユ棩鏈燂紝nnn 涓哄悓涓� X鍙�+鍚屼竴T闃熶笓涓�+鍚屼竴涓婃姤鏃ユ湡鐨勫簭鍙凤紙浠� 001 璧凤級銆傝〃鍗曚腑璇ュ瓧娈靛彧璇伙紝鏄剧ず鈥滀繚瀛樺悗鑷姩鐢熸垚鈥濄��</div><div class="label">鏂板琛ㄥ崟浜や簰锛�2026-09-16 纭锛夛細</div><div class="note2">鐐瑰嚮鈥滄柊澧炩�濆脊鍑鸿〃鍗曪紝涓嶄娇鐢ㄥ師闇�姹傜殑琛ㄦ牸寮忓綍鍏ャ��</div><div class="note2">榛樿鍊硷細涓婃姤鍗曚綅=鐧诲綍鐢ㄦ埛鎵�灞炲崟浣嶏紱T闃熶笓涓�=鐧诲綍鐢ㄦ埛鎵�灞炰笓涓氾紱璐熻矗浜�=鐧诲綍鐢ㄦ埛锛涗笂鎶ユ棩鏈�=褰撳墠鏃ユ湡銆�</div><div class="note2">涓嬫媺瀛楁锛歍闃熶笓涓氥�佸伐绋嬩笓涓氥�佹晠闅滃奖鍝嶃�佹晠闅滄�ц川銆�</div><div class="note2">鐐瑰嚮鈥滆澶囧悕绉扳�濇墦寮�浜у搧缁撴瀯鏍戯紱閫夋嫨瑁呭锛堣澶囷級鍚庯紝鑷姩濉叆鈥滆澶囧悕绉扳�濆拰鈥滄墍灞炰笂绾х郴缁熲�濄��</div><div class="note2">H 涓烘晱鎰熻瘝瀛楀吀浠gО锛屽瓧娈电粺涓�鍐欌�滄槸鍚︽秹H鈥濄��</div></section><section class="card"><h3>AXU4 路 XCB鍔╃悊瀹℃牳锛堟枃妗i〉 10锛�</h3><div class="note">椤甸潰瀹氫綅锛氱畝鍖栨祦绋嬬 2 姝モ�淴CB鍔╃悊瀹℃牳鈥濄��</div><div class="note">鍛藉悕鍙e緞锛�2026-09-16 纭锛夛細椤甸潰鍙婅彍鍗曞悕绉扮粺涓�鍐欌�淴CB鍔╃悊瀹℃牳鈥濓紝椤甸潰鍐呭師鈥滃鎵光�濇枃妗堢粺涓�鏀逛负鈥滃鏍糕�濄��</div><div class="note2">銆愬凡纭 2026-09-16銆戞湰鐜妭瀹屾垚锛氬垎閰嶆壙淇崟浣嶃�佹寜鎵夸慨鍗曚綅鎷嗗崟銆佺敓鎴愬伐绋嬪崟鍙凤紱XCB鍔╃悊瀹℃牳瀹屾垚鍚庡嵆鍙鍣ㄦ潗銆侸CC澶勯暱瀹℃壒瀹屾垚鍚庣洿鎺ヨ繘鍏CB鍔╃悊娲惧崟椤甸潰锛涢〉闈㈡樉绀烘湭娲惧崟鏁呴殰锛屽嬀閫夊悗涓�閿淳鍗曞苟鎸夋壙淇崟浣嶇敓鎴愭淳宸ュ崟锛屼笉鍐嶅垎閰嶆垨淇敼鎵夸慨鍗曚綅銆�</div><div class="label">涓氬姟瑙勫垯</div><div class="note2">涓�涓晠闅滃崟鍙互鍒嗛厤澶氫釜鎵夸慨鍗曚綅锛涘鏍稿畬鎴愬悗鎸夋壙淇崟浣嶆媶鎴愬涓伐绋嬪崟璧板悗缁祦绋嬨��</div><div class="note2">宸ョ▼鍗曞彿鍦ㄦ湰鐜妭鐢熸垚锛岃鍒欙細鍥哄畾-X鍙�/鐝粍绫诲埆-YYMMDD-鍘傚浠e彿/搴忓彿锛屽叾涓� YYMMDD 涓轰笂鎶ユ棩鏈熴��</div><div class="note2">鏈纭畾鏄惁鐩戞祴椤圭洰銆佺洃娴嬫暟鍊肩瓑銆�</div><div class="label">鏌ヨ鏉′欢</div><div class="chips"><span class="td">宸ョ▼閫氱敤鏉′欢</span><span class="td">鎵夸慨鍗曚綅</span><span class="td">鏁呴殰鎬ц川</span><span class="td">浠诲姟鐘舵��</span></div><div class="label">鎸夐挳/鎿嶄綔</div><div class="chips"><span class="td">鏌ヨ</span><span class="td">鍒锋柊</span><span class="td">淇敼</span><span class="td">閿�椤癸紙浠匵CB鍔╃悊锛�</span><span class="td">瀹℃牳閫氳繃/涓嶉�氳繃</span><span class="td"><mark>宸插幓鎺夛細娣诲姞杩涘害澶囨敞銆丆MB宸查槄</mark></span></div><div class="note2">浠诲姟鐘舵�佺粺涓�閲囩敤鍏ㄥ眬涓冧釜鍊硷細鏈笂鎶� / 宸蹭笂鎶CB / 涓婄骇鎵撳洖 / XCB鍔╃悊瀹℃牳瀹屾垚 / CMB棰嗗瀹℃壒瀹屾垚 / JCC鍔╃悊瀹℃壒瀹屾垚 / JCC澶勯暱宸查槄銆�</div><div class="label">鍒楄〃鍒楋紙琛ㄥご锛�</div><div class="chips"><span class="td">搴忓彿</span><span class="td">鎿嶄綔</span><span class="td">浠诲姟鐘舵��</span><span class="td">鎵�灞炰笂绾х郴缁�</span><span class="td">瑁呭鍚嶇О</span><span class="td">宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�</span><span class="td">淇悊鎶�鏈姹�</span><span class="td">宸ョ▼涓撲笟</span><span class="td">T闃熶笓涓�</span><span class="td">璺熻釜鎶�鏈槦</span><span class="td">璐熻矗浜�</span><span class="td">鏁呴殰褰卞搷</span><span class="td">鏁呴殰鎬ц川</span><span class="td">閲嶅ぇ鏁呴殰鎻忚堪</span><span class="td">鏄惁鑷慨</span><span class="td">鏄惁娑塇</span><span class="td">鐩戞祴椤圭洰</span><span class="td">鐩戞祴鏁板��</span><span class="td">瑕佹眰瀹屽伐鏃ユ湡</span><span class="td">澶囨敞</span></div><div class="label">瀹℃牳琛ㄥ崟瀛楁锛�2026-09-16 纭锛夛細</div><div class="chips"><span class="td">T闃熶笓涓�</span><span class="td">涓婃姤鍗曚綅</span><span class="td">璐熻矗浜�</span><span class="td">瑁呭鍚嶇О</span><span class="td">涓婃姤鏃ユ湡</span><span class="td">宸ョ▼鍚嶇О</span><span class="td">鎵夸慨鍗曚綅锛堝彲鍒嗛厤澶氫釜锛涘�欓�夋潵鑷�滅郴缁熺鐞� > 鎵夸慨鍗曚綅绠$悊鈥濓紝鎸夆�滄帓搴忊�濆彇鍓�7涓父鐢ㄦ壙淇崟浣嶆樉绀猴級</span><span class="td">璺熻釜鎶�鏈槦锛堜粠鏁呴殰淇℃伅甯﹀嚭锛屽崟閫夛細涓�闃�/浜岄槦/涓夐槦锛�</span><span class="td">瑕佹眰瀹屽伐鏃ユ湡</span><span class="td">鏁呴殰鎬ц川</span><span class="td">鏄惁鑷慨</span><span class="td">鏄惁娑塇</span><span class="td">鏄惁鐩戞祴椤圭洰</span><span class="td">閲嶅ぇ鏁呴殰鎻忚堪</span><span class="td">鐩戞祴鏁版嵁鍊�</span><span class="td">澶囨敞</span></div><div class="note2">瀹℃牳琛ㄥ崟鍖呭惈閫氳繃銆佷笉閫氳繃銆佸叧闂�俋CB鍔╃悊瀹℃牳涓嶉�氳繃鏃朵笉杞�滀笂绾ф墦鍥炩�濓細鍐呭涓嶅悎鏍肩敱XCB鍔╃悊閫氳繃鈥滀慨鏀光�濇寜閽嚜琛屼慨姝o紱涓嶄綔涓烘晠闅滃鐞嗘椂鐐光�滈攢椤光�濄��</div><div class="note2">閿�椤规潈闄愪笌瀛楁锛�2026-09-17 纭锛夛細鏁呴殰涓婃姤闃舵鐨勯攢椤圭粺涓�鐢盭CB鍔╃悊鎵ц锛涜褰曢攢椤圭悊鐢便�侀攢椤逛汉銆侀攢椤规椂闂淬�傞攢椤逛汉鑷姩鍙栧綋鍓嶇櫥褰昘CB鍔╃悊锛岄攢椤规椂闂磋嚜鍔ㄥ彇绯荤粺褰撳墠鏃堕棿銆�</div></section><section class="card"><h3>AXU5 路 CMB棰嗗瀹℃壒锛堟枃妗i〉 11锛�</h3><div class="note">鍒楄〃 + 鎵归噺瀹℃壒</div><div class="note2">AXU5/6/7 鍏遍�氾紙2026-09-17 纭锛夛細淇濈暀鍒楄〃銆佸埛鏂般�佹壒閲忓鎵癸紱鍘绘帀淇敼銆丆MB宸查槄銆侀攢椤广�佹坊鍔犺繘搴﹀娉紱涓夐〉缁熶竴浣跨敤鈥滃鎵光�濈敤璇��</div><div class="label">鎸夐挳/鎿嶄綔</div><div class="chips"><span class="td">鍒楄〃</span><span class="td">鍒锋柊</span><span class="td">鎵归噺瀹℃壒</span><span class="td"><mark>宸插幓鎺夛細淇敼銆丆MB宸查槄銆侀攢椤广�佹坊鍔犺繘搴﹀娉�</mark></span></div><div class="label">鍒楄〃鍒楋紙琛ㄥご锛�</div><div class="chips"><span class="td">搴忓彿</span><span class="td">鎿嶄綔</span><span class="td">浠诲姟鐘舵��</span><span class="td">鎵�灞炰笂绾х郴缁�</span><span class="td">瑁呭鍚嶇О</span><span class="td">宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�</span><span class="td">淇悊鎶�鏈姹�</span><span class="td">宸ョ▼涓撲笟</span><span class="td">T闃熶笓涓�</span><span class="td">璺熻釜鎶�鏈槦</span><span class="td">璐熻矗浜�</span><span class="td">鏁呴殰褰卞搷</span><span class="td">鏁呴殰鎬ц川</span><span class="td">閲嶅ぇ鏁呴殰鎻忚堪</span><span class="td">鏄惁鑷慨</span><span class="td">鏄惁娑塇</span><span class="td">鐩戞祴椤圭洰</span><span class="td">鐩戞祴鏁板��</span><span class="td">瑕佹眰瀹屽伐鏃ユ湡</span><span class="td">澶囨敞</span></div><div class="note2">浠诲姟鐘舵�侊細缁熶竴閲囩敤鍏ㄥ眬涓冧釜鍊硷紱鏈〉瀹℃壒涓嶉�氳繃鏃剁洿鎺ヨ浆涓衡�滀笂绾ф墦鍥炩�濆苟閫�鍥瀀CB鍔╃悊銆�</div><div class="note2">鏁呴殰淇℃伅瀹℃壒瀛楁缁勶紙2026-09-17 纭锛夛細CMB棰嗗瀹℃壒鎰忚銆丆MB棰嗗瀹℃壒缁撴灉锛堥�氳繃/涓嶉�氳繃锛夈�丆MB棰嗗瀹℃壒鏃堕棿锛汮CC鍔╃悊瀹℃壒鎰忚銆丣CC鍔╃悊瀹℃壒缁撴灉锛堥�氳繃/涓嶉�氳繃锛夈�丣CC鍔╃悊瀹℃壒鏃堕棿锛汮CC澶勯暱瀹℃壒鎰忚銆丣CC澶勯暱瀹℃壒缁撴灉锛堥�氳繃/涓嶉�氳繃锛夈�丣CC澶勯暱瀹℃壒鏃堕棿銆�</div><div class="label">瀹℃壒琛ㄥ崟锛�</div><div class="chips"><span class="td">瀹℃壒鎰忚</span><span class="td">鍏抽棴 / 閫氳繃 / 涓嶉�氳繃</span></div></section><section class="card"><h3>AXU6 路 JCC鍔╃悊瀹℃壒锛堟枃妗i〉 12锛�</h3><div class="note">涓� AXU5 鍏辩敤鍚屼竴濂楃晫闈㈠拰鍔熻兘锛�2026-09-17 纭锛�</div><div class="note2">涓婄骇鎵撳洖瑙勫垯锛欽CC鍔╃悊瀹℃壒涓嶉�氳繃鏃剁洿鎺ラ��鍥瀀CB鍔╃悊锛屼换鍔$姸鎬佽浆涓衡�滀笂绾ф墦鍥炩�濓紝涓嶉�愮骇鎵撳洖銆�</div><div class="label">鎸夐挳/鎿嶄綔</div><div class="chips"><span class="td">鍒楄〃</span><span class="td">鍒锋柊</span><span class="td">鎵归噺瀹℃壒</span><span class="td"><mark>宸插幓鎺夛細淇敼銆丆MB宸查槄銆侀攢椤广�佹坊鍔犺繘搴﹀娉�</mark></span></div><div class="label">鍒楄〃鍒楋紙琛ㄥご锛�</div><div class="chips"><span class="td">搴忓彿</span><span class="td">鎿嶄綔</span><span class="td">浠诲姟鐘舵��</span><span class="td">鎵�灞炰笂绾х郴缁�</span><span class="td">瑁呭鍚嶇О</span><span class="td">宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�</span><span class="td">淇悊鎶�鏈姹�</span><span class="td">宸ョ▼涓撲笟</span><span class="td">T闃熶笓涓�</span><span class="td">璺熻釜鎶�鏈槦</span><span class="td">璐熻矗浜�</span><span class="td">鏁呴殰褰卞搷</span><span class="td">鏁呴殰鎬ц川</span><span class="td">閲嶅ぇ鏁呴殰鎻忚堪</span><span class="td">鏄惁鑷慨</span><span class="td">鏄惁娑塇</span><span class="td">鐩戞祴椤圭洰</span><span class="td">鐩戞祴鏁板��</span><span class="td">瑕佹眰瀹屽伐鏃ユ湡</span><span class="td">澶囨敞</span></div></section><section class="card"><h3>AXU7 路 JCC澶勯暱瀹℃壒锛堟枃妗i〉 13锛�</h3><div class="note">涓� AXU5/6 鍏辩敤鍚屼竴濂楃晫闈㈠拰鍔熻兘锛�2026-09-17 纭锛夛紱椤甸潰鍙婅彍鍗曞悕绉扮粺涓�涓衡�淛CC澶勯暱瀹℃壒鈥�</div><div class="note2">涓婄骇鎵撳洖瑙勫垯锛欽CC澶勯暱瀹℃壒涓嶉�氳繃鏃剁洿鎺ラ��鍥瀀CB鍔╃悊锛屼换鍔$姸鎬佽浆涓衡�滀笂绾ф墦鍥炩�濓紝涓嶉�愮骇鎵撳洖銆�</div><div class="label">鎸夐挳/鎿嶄綔</div><div class="chips"><span class="td">鍒楄〃</span><span class="td">鍒锋柊</span><span class="td">鎵归噺瀹℃壒</span><span class="td"><mark>宸插幓鎺夛細淇敼銆丆MB宸查槄銆侀攢椤广�佹坊鍔犺繘搴﹀娉�</mark></span></div><div class="label">鍒楄〃鍒楋紙琛ㄥご锛�</div><div class="chips"><span class="td">搴忓彿</span><span class="td">鎿嶄綔</span><span class="td">浠诲姟鐘舵��</span><span class="td">鎵�灞炰笂绾х郴缁�</span><span class="td">瑁呭鍚嶇О</span><span class="td">宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�</span><span class="td">淇悊鎶�鏈姹�</span><span class="td">宸ョ▼涓撲笟</span><span class="td">T闃熶笓涓�</span><span class="td">璺熻釜鎶�鏈槦</span><span class="td">璐熻矗浜�</span><span class="td">鏁呴殰褰卞搷</span><span class="td">鏁呴殰鎬ц川</span><span class="td">閲嶅ぇ鏁呴殰鎻忚堪</span><span class="td">鏄惁鑷慨</span><span class="td">鏄惁娑塇</span><span class="td">鐩戞祴椤圭洰</span><span class="td">鐩戞祴鏁板��</span><span class="td">瑕佹眰瀹屽伐鏃ユ湡</span><span class="td">澶囨敞</span></div></section><section class="card"><h3>AXU8 路 涓存姠淇伐绋嬬鐞嗭紙鎬昏锛夛紙鏂囨。椤� 14锛�</h3><div class="note">椤甸潰璇存槑锛氣�滅被浼煎凡娲惧伐绋嬪崟锛岃鑹蹭笉涓�鏍凤紝鎵嬪伐鎿嶄綔鈥�</div><div class="label">鐘舵�佸叆鍙� / 缁熻</div><div class="chips"><span class="td">瀹屽伐</span><span class="td">鏈畬宸�</span><span class="td">寰呰瘯楠�</span><span class="td">寰呭浠�</span><span class="td"><mark>宸茬‘璁や负宸ョ▼鐘舵�佺瓫閫夊叆鍙o紙鎸夐挳/椤电锛夛紝褰㈠紡寰呭畾</mark></span></div><div class="note2">涓嬫柟绀烘剰鎬ц〃鏍硷細琛ㄥご浠� Column1/Column2/Column3 + 2 琛岀┖琛岋紙浣滆�呮湭濉瓧娈碉級鈥斺�旀寜鐘舵�佺瓫閫夊睍绀哄伐绋嬪垪琛紝瀛楁鍚庣画鑷璁捐</div></section><section class="card"><h3>AXU9 路 CMB棰嗗宸查槄锛堟枃妗i〉 15锛�</h3><div class="note"><mark>鏆備笉澶勭悊 2026-09-20锛氫笉绾冲叆褰撳墠闇�姹備笌鍚庣画鏍稿銆�</mark></div><div class="note">椤甸潰璇存槑锛氣�滄煡鐪婥MB棰嗗宸叉壒闃呯殑鈥濆伐绋�</div><div class="label">鍒楄〃鍒楋紙琛ㄥご锛�</div><div class="chips"><span class="td">搴忓彿</span><span class="td">浠诲姟鐘舵��</span><span class="td">鎵�灞炰笂绾х郴缁�</span><span class="td">瑁呭鍚嶇О</span><span class="td">宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�</span><span class="td">淇悊鎶�鏈姹�</span><span class="td">宸ョ▼涓撲笟</span><span class="td">T闃熶笓涓�</span><span class="td">璺熻釜鎶�鏈槦</span><span class="td">璐熻矗浜�</span><span class="td">鏁呴殰褰卞搷</span><span class="td">鏁呴殰鎬ц川</span><span class="td">閲嶅ぇ鏁呴殰鎻忚堪</span><span class="td">鏄惁鑷慨</span><span class="td">鏄惁娑塇</span><span class="td">鐩戞祴椤圭洰</span><span class="td">鐩戞祴鏁板��</span><span class="td">瑕佹眰瀹屽伐鏃ユ湡</span><span class="td">澶囨敞</span><span class="td">JCC鍔╃悊瀹℃壒锛堝惁锛�</span><span class="td">JCC澶勯暱瀹℃壒</span><span class="td">瀹℃壒瀹屾垚</span></div><div class="label">鎸夐挳/鎿嶄綔</div><div class="chips"><span class="td">閿�椤�</span></div><div class="note2"><mark>JCC鍔╃悊瀹℃壒/JCC澶勯暱瀹℃壒/瀹℃壒瀹屾垚 涓夊垪鍚箟寰呯‘璁�</mark></div><div class="note2">娴佺▼浣嶇疆锛�2026-09-20 纭锛夛細JCC澶勯暱瀹℃壒瀹屾垚鍚庣洿鎺ヨ繘鍏CB鍔╃悊娲惧崟锛汚XU9涓嶄綔涓哄繀缁忔祦绋嬭妭鐐广��</div></section><section class="card"><h3>AXU10 路 娲惧崟绠$悊锛堟枃妗i〉 16锛�2026-09-20 鎸夌敤鎴峰彛寰勯噸瀹氫箟锛�</h3>
 <div class="note2">椤甸潰鍏ュ彛锛欽CC澶勯暱瀹℃壒瀹屾垚鍚庯紝娴佺▼鐩存帴杩涘叆 XCB鍔╃悊娲惧崟椤甸潰銆�</div>
 <div class="note2">椤甸潰鍐呭锛氭樉绀衡�滄湭娲惧崟鈥濈殑鏁呴殰涓�瑙堛��</div>

--
Gitblit v1.9.1