HANDOFF.md
@@ -404,12 +404,12 @@ - æ¬ä¼è¯è¿å±ï¼ç¨æ·ç»åº **AXU1 æµç¨æ»è§çå£å¾åæ´**ââ第ä¸ç**䏿åå§éæ±**ï¼æ¹ä¸º **â ç®åä¸å¡æµç¨ + â¡ åªæä¾ç½ç»çã䏿ä¾ç¦»çº¿åæºç**ã - **第ä¸çç®åæµç¨ï¼10 æ¥ï¼å·²ç¡®è®¤ï¼2026-09-20 ç»åéªæ¶é¾ï¼**ï¼Tåæ é䏿¥ â XCBå©çå®¡æ ¸ï¼**åé æ¿ä¿®åä½ãæåãçæå·¥ç¨åå·**ï¼â CMBé¢å¯¼å®¡æ¹ â JCCå©çå®¡æ¹ â JCCå¤é¿å®¡æ¹ â XCBå©çæ´¾åï¼**æ¾ç¤ºæªæ´¾åæ éï¼å¾éåä¸é®æ´¾åï¼ææ¿ä¿®åä½çææ´¾å·¥å**ï¼â å家线ä¸ä¿®ç â å家䏿¥å®å·¥ä¿¡æ¯ â å®å·¥éªæ¶æµç¨ï¼éªæ¶ç³è¯· â è´¨æ£åéªæ¶ â è´¨æ£è´è´£äººéªæ¶ â å®å·¥éªæ¶æå°è¾åº â ä¿®åè´¨éè¯çº§ï¼â éªæ¶å®æã - è¿å¸¦å£å¾åæ´ï¼â åæ¶èé¿å®¡æ¹ç¯èï¼åå¾ ç¡®è®¤æ¸ å第 9 æ¡ â 已解å³ï¼ä¸çº³å ¥ç¬¬ä¸çï¼ï¼â¡ åæ¶å ¨é¨ç¦»çº¿è½åï¼åæºç/离线填åãæ°æ®å å¯¼å ¥å¯¼åºï¼ï¼â¢ åæ¶âXCBå©çå®å·¥å¤ç/导åºå®å·¥é¡¹ç®âç¯èï¼â£ æ¥ä»·å¤çç§»åºä¸»æµç¨ï¼â¤ JCCå©ç审æ¹ç¡®è®¤ä¸ºå¿ ç»ç¯èã - è¿å¸¦å£å¾åæ´ï¼â åæ¶Té¿å®¡æ¹ç¯èï¼åå¾ ç¡®è®¤æ¸ å第 9 æ¡ â 已解å³ï¼ä¸çº³å ¥ç¬¬ä¸çï¼ï¼â¡ åæ¶å ¨é¨ç¦»çº¿è½åï¼åæºç/离线填åãæ°æ®å å¯¼å ¥å¯¼åºï¼ï¼â¢ åæ¶âXCBå©çå®å·¥å¤ç/导åºå®å·¥é¡¹ç®âç¯èï¼â£ æ¥ä»·å¤çç§»åºä¸»æµç¨ï¼â¤ JCCå©ç审æ¹ç¡®è®¤ä¸ºå¿ ç»ç¯èã - 已忥è½çï¼`requirement\çé¢è¿å.html`ï¼æ°å¢å ¨å±å£å¾æ¡ + 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`ï¼å ¨å±å£å¾ãAXU1 è¡å·²æ ¸å¯¹ãC-8 已解å³ãæ°å¢ C-12âC-15ï¼ã - 彿¶å¾ åçåç»å¤çï¼C-12/C-13/C-14/C-15 å已解å³ï¼AXU3/AXU4 å·²äº 2026-09-16 æ ¸å¯¹å®æï¼A ç»ä¸ä¸æ¥ä¸º AXU5ï¼B ç»å·¥ç¨ç¶ææº 10 项ä»å¾ çã **2026-09-14 ç¨æ·çå¤ï¼å·²åå¡«å ¨é¨ææ¡£ï¼**ï¼ - â èé¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼ç¡®è®¤ï¼ï¼â¡ æ¥ä»·å¤çç§»åºä¸»æµç¨ï¼âæ¥ä»·ç®¡çâèå䏿¨¡åæ´ä½ä¸åï¼C-14 已解å³ï¼ï¼â¢ JCC å©ç审æ¹ä¸ºå¿ ç»ç¯èï¼ç¡®è®¤ï¼ï¼â£ C-12 离线/æ°æ®å ç¸å ³åè½**æ´ä½åæ¶ï¼å¯¹åºèå项ä¸å¹¶å é¤**ï¼æ°æ®å å¯¼å ¥ãå®å·¥ä¿¡æ¯å¯¼å ¥ã导åºå¾ å®å·¥æ°æ®å ãæ¥ä»·å¯¼åº/å¯¼å ¥ï¼ï¼â¤ AXU2 æ°æ®å å¯¼å ¥ï¼åªåç½ç»çãæ å¯¼å ¥åè½ï¼**æ éæ ¸å¯¹**ï¼é¡µé¢ä¸èå项å é¤ï¼ã - â Té¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼ç¡®è®¤ï¼ï¼â¡ æ¥ä»·å¤çç§»åºä¸»æµç¨ï¼âæ¥ä»·ç®¡çâèå䏿¨¡åæ´ä½ä¸åï¼C-14 已解å³ï¼ï¼â¢ JCC å©ç审æ¹ä¸ºå¿ ç»ç¯èï¼ç¡®è®¤ï¼ï¼â£ C-12 离线/æ°æ®å ç¸å ³åè½**æ´ä½åæ¶ï¼å¯¹åºèå项ä¸å¹¶å é¤**ï¼æ°æ®å å¯¼å ¥ãå®å·¥ä¿¡æ¯å¯¼å ¥ã导åºå¾ å®å·¥æ°æ®å ãæ¥ä»·å¯¼åº/å¯¼å ¥ï¼ï¼â¤ AXU2 æ°æ®å å¯¼å ¥ï¼åªåç½ç»çãæ å¯¼å ¥åè½ï¼**æ éæ ¸å¯¹**ï¼é¡µé¢ä¸èå项å é¤ï¼ã - 已解å³ï¼**C-13 æ´¾åå£å¾**ï¼2026-09-16 确认ï¼ââXCBå©çå®¡æ ¸ä»è´è´£åé æ¿ä¿®åä½ãæåãçæå·¥ç¨åå·ï¼åç»æ´¾åé¡µææ ééæ©ãææ¿ä¿®åä½çææ´¾å·¥åï¼ä¸åéæ°åé æ¿ä¿®åä½æä¿®æ¹åå®¶ãå©ä½ï¼B ç»å·¥ç¨ç¶ææº 10 项ï¼A ç»ä¸ä¸æ¥ AXU5 CMBé¢å¯¼å®¡æ¹ãï¼C-15 æ¯è¯å·²äº 2026-09-14 çå¤ï¼ç»ä¸åâTåéªæ¶âï¼ - åç»è¿å±ï¼A ç»å·²äº 2026-09-16 宿 AXU3/AXU4ï¼ä¸ä¸æ¥ä¸º AXU5 CMBé¢å¯¼å®¡æ¹ï¼AXU0/AXU1/AXU15 已宿ï¼AXU2 å æ ¸å¯¹ï¼ã @@ -469,7 +469,7 @@ å½åçº³å ¥æ¨¡åï¼XCBå©çæ´¾åï¼AXU10ï¼â å®å·¥æ åµå¤çï¼AXU14ï¼â å®å·¥ä¿¡æ¯ä¸æ¥ï¼AXU15ï¼â å®å·¥éªæ¶ï¼éªæ¶ç³è¯· â è´¨æ£åéªæ¶ â è´¨æ£è´è´£äººéªæ¶ â å®å·¥éªæ¶æå°è¾åº â ä¿®åè´¨éè¯çº§ï¼â ç³»ç»ç®¡ç â ç»è®¡æ¥è¡¨ãå临æ¢ä¿®å·¥ç¨ç®¡çä¸ç CMBé¢å¯¼å·²é ï¼AXU9ï¼ã已派工ç¨ï¼AXU11ï¼ã已派工ç¨åï¼AXU12ï¼ãå·¥ç¨åæå°è¾åºï¼AXU13ï¼ä»¥åææ¯æä»¶ç®¡çï¼AXU16ï¼æä¸å¤çï¼çæµä¿¡æ¯ãæ¥è¯¢ãåºç¡ä¿¡æ¯åææ¯æä»¶ç®¡çä¸å主å¨å±å¼ï¼é¤éç¨æ·åç»è¡¥å éæ±ã åå§éæ±æµç¨ï¼åæ¡£ï¼å·²ä¸éç¨ï¼ï¼æ é䏿¥ï¼æ°æ®å å¯¼å ¥ï¼â èé¿ï¼æ¹é/å项ï¼â 大éå©çï¼XCBï¼å®¡æ¹ â CMBé¢å¯¼å®¡æ¹ â JCCå©çå®¡æ¹ â JCCå¤é¿å®¡æ¹ â â¦â¦ â å®å·¥ä¿¡æ¯ä¸æ¥ â éªæ¶ï¼å«åæºç/ç¦»çº¿æ°æ®å 䏿¥ä»·å¯¼åºå¯¼å ¥ç¯èã åå§éæ±æµç¨ï¼åæ¡£ï¼å·²ä¸éç¨ï¼ï¼æ é䏿¥ï¼æ°æ®å å¯¼å ¥ï¼â Té¿ï¼æ¹é/å项ï¼â 大éå©çï¼XCBï¼å®¡æ¹ â CMBé¢å¯¼å®¡æ¹ â JCCå©çå®¡æ¹ â JCCå¤é¿å®¡æ¹ â â¦â¦ â å®å·¥ä¿¡æ¯ä¸æ¥ â éªæ¶ï¼å«åæºç/ç¦»çº¿æ°æ®å 䏿¥ä»·å¯¼åºå¯¼å ¥ç¯èã å ³é®è§åï¼æ¥èªæªå¾å¤æ³¨ï¼ï¼ - æ´¾åè§åï¼2026-09-20 确认ï¼ï¼JCCå¤é¿å®¡æ¹å®æåè¿å ¥XCBå©çæ´¾å页é¢ï¼æ¾ç¤ºâæªæ´¾åâçæ éä¸è§ï¼å¾éä¸ä¸ªæå¤ä¸ªæ éåç¹å»âä¸é®æ´¾åâï¼ç³»ç»ææ¯ä¸ªæ éçæ¿ä¿®åä½çææ´¾å·¥åãä¸ä¸ªæ éæå¤ä¸ªæ¿ä¿®å使¶ï¼æ¯ä¸ªæ¿ä¿®åä½åçæä¸å¼ 派工åã @@ -517,7 +517,7 @@ 6. ãå·²è§£å³ 2026-09-20ãâ鮿´¾åâ确认åºä¸ºâä¸é®æ´¾åâï¼ä¿®æ¹ãçæä¸´ä¿®å·¥ç¨åãå é¤äºå¤ï¼çä¸ºæ¸ é¤éå¤ï¼çåæªå¾ä¸å¡æé®æä¸éè¦ã 7. æ æªå¾æ¨¡å页é¢è®¾è®¡ï¼çæµä¿¡æ¯ãæ¥è¯¢ï¼å·¥ç¨å/å·¥ç¨é¡¹ç®/æ éä¿¡æ¯/é项ï¼ãæ¥ä»·ç®¡çï¼å¯¼åºå¾ æ¥ä»·/ä¿®è¹åå审/JCCå®¡æ ¸ï¼ãåºç¡ä¿¡æ¯ï¼JCä¿¡æ¯/ç¨æ·æ°æ®èå´ï¼ï¼ç³»ç»ç®¡çèå已确认å 嫿¿ä¿®åä½ç®¡çã产åç»ææ 管çã设å¤åºç¡åºç®¡çï¼å®å·¥éªæ¶5èå已确认ï¼å段ä¸é¡µé¢ç»èä»å¾ 设计ã 8. ã已解å³ãé¦é¡µç»è®¡å¡ç宿´æ¸ åï¼2026-09-10ï¼ï¼å®ä¸ºâæXå·ç»è®¡è¡¨â表头 11 åï¼Xå·/å½åç¶æ/åé æ³ä½/TD/å·¥ç¨æ»æ°/éé¡¹æ»æ°/é大æ é/æªå®å·¥/å®å·¥å¾ è¯éª/å·²å®å·¥/å¾ å¤çï¼ï¼éæ£åå¡çï¼âJCä¿¡æ¯â=è°è¹ä¿¡æ¯ï¼2026-08-26ï¼ãTD=èéï¼2026-09-10ï¼å已确认ã 9. ãå·²è§£å³ 2026-09-11ãèé¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼ç®åæµç¨ä» Tåæ é䏿¥ç´æ¥è¿å ¥ XCBå©çå®¡æ ¸ï¼ã 9. ãå·²è§£å³ 2026-09-11ãTé¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼ç®åæµç¨ä» Tåæ é䏿¥ç´æ¥è¿å ¥ XCBå©çå®¡æ ¸ï¼ã 10. åå ¸æ°æ®ï¼æ éå½±åï¼ç¤ºä¾â便°âï¼ãæ éæ§è´¨ï¼ç¤ºä¾âé大âï¼å·²ç¡®è®¤æå¯é ç½®åå ¸åæ®µå¤çï¼å ¶ä»åå¼å¾ è¡¥ï¼Xå·âå½åç¶æâå ¶ä»åå¼å¾ è¡¥ã 11. æ¯è¯å误ï¼OCRï¼ï¼ç½ä¿®=èªä¿®ãTåééªæ¶=Tåéªæ¶ï¼2026-09-14 确认ç»ä¸åâTåéªæ¶âï¼ï¼H 为ææè¯ä»£ç§°ï¼é坿æç»ä¸åâæ¯å¦æ¶Hâï¼2026-09-16ï¼ã 12. ãå¾ å·æéç¨æ·ãå¨æç³»ç»å¯¹æ¥ææ¡£ï¼ç¨æ·å·²æææ¡£ï¼éè¦æ¶åç¨æ·ç´¢åï¼å¨æç®å½è¡¨ç»æã卿IDãæ¥å£æ¹å¼çï¼ã @@ -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` + æ¬æä»¶ï¼æ ä¸´æ¶æä»¶æ®çï¼å·²æ ¸éªï¼ï¼æ è¿è¡ä¸çæªä¿åå·¥ä½ã - **æ¬æ¬¡å·²è§£å³çå¾ ç¡®è®¤é¡¹**ï¼C-8ï¼èé¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼ãC-12ï¼ç¦»çº¿/æ°æ®å åè½æ´ä½åæ¶ + 对åºèå项å é¤ï¼ãC-14ï¼æ¥ä»·ç®¡çèå䏿¨¡åæ´ä½ä¸åï¼ãC-15ï¼æ¯è¯ç»ä¸åâTåéªæ¶âï¼ï¼AXU2 æ°æ®å å¯¼å ¥å æ ¸å¯¹ï¼é¡µé¢åæ¶ï¼ã - **æ¬æ¬¡å·²è§£å³çå¾ ç¡®è®¤é¡¹**ï¼C-8ï¼Té¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼ãC-12ï¼ç¦»çº¿/æ°æ®å åè½æ´ä½åæ¶ + 对åºèå项å é¤ï¼ãC-14ï¼æ¥ä»·ç®¡çèå䏿¨¡åæ´ä½ä¸åï¼ãC-15ï¼æ¯è¯ç»ä¸åâTåéªæ¶âï¼ï¼AXU2 æ°æ®å å¯¼å ¥å æ ¸å¯¹ï¼é¡µé¢åæ¶ï¼ã - **仿ªçå¤ï¼ä¸æ¬¡å¼å·¥ä»è¿éæ¥ï¼**ï¼ 1. C-13 æ´¾åå£å¾å·²äº 2026-09-16 è§£å³ï¼XCBå©çå®¡æ ¸è´è´£åé æ¿ä¿®åä½ãæåãçæå·¥ç¨åå·ï¼æ´¾åé¡µææ ééæ©ãææ¿ä¿®åä½çææ´¾å·¥åã 2. AXU3/AXU4 å·²äº 2026-09-16 宿ï¼A ç»ä¸ä¸æ¥ä¸º AXU5 CMBé¢å¯¼å®¡æ¹ã @@ -830,7 +830,7 @@ ## 27. æ¶å°¾å¿«ç §ï¼2026-10-07 å¨ä¸ï¼CMB 审æ¹å JCC å©çå表æªå·æ°ï¼ **é®é¢ä¸æ ¹å ** - `rk_CMBé¢å¯¼å®¡æ¹` éè¿åè¿å ¥ `rk_JCCå©ç审æ¹`ï¼é¡µé¢å¯è½ä¸æ¾ç¤ºå·²è¿å ¥ `CMB_APPROVED` ç¶æçæ°æ®ã - `rk_第äºå®¡æ¹äººå®¡æ¹` éè¿åè¿å ¥ `rk_第ä¸å®¡æ¹äººå®¡æ¹`ï¼é¡µé¢å¯è½ä¸æ¾ç¤ºå·²è¿å ¥ `CMB_APPROVED` ç¶æçæ°æ®ã - CMBãJCC å©çãJCC å¤é¿ä¸ä¸ªå®¡æ¹è·¯ç±å ±ç¨ `SimpleApproval.vue`ï¼å¹¶å ±ç¨åä¸ä¸ª keep-alive ç¼åå `SimpleApproval`ãè·¯ç±åæ¢æ¶ç»ä»¶å®ä¾è¢«å¤ç¨ï¼ä½å代ç åªå¨ `created()` æ¥è¯¢ä¸æ¬¡ï¼æªå¨ `stage` ååæ¶éæ°å è½½ã **ä¿®å¤å 容** @@ -1040,3 +1040,288 @@ - å¼åç¯å¢ææè¯é ç½®ä¼æ `rk_T` æ¾ç¤ºä¸ºâ梯âï¼å æ¤é¡µé¢æªå¾ä¸å¯è½æ¾ç¤ºâè´è´£æ¢¯âï¼ä»£ç ååç«¯åæ®µä»æ¯ `rk_T`ã - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå ¬å¸ Git ä»åº `main` 忝ã ## 27. æ¶å°¾å¿«ç §ï¼2026-10-09 å¨äºï¼éæ± 32 å·¥ç¨åæå°ä¿®æ£ä¸éªè¯ï¼ **æ¬è½®ä¿®æ£å 容** - å·¥ç¨å Word 模æ¿çæå³åç¬ç«ä¸º `UnitNo` æ ï¼å宽 `400` twipsï¼è¯¥åè·¨è头ãè¡¨å¤´åæ°æ®è¡çºµååå¹¶ï¼åå æ ¼æåæ¹å为 `btLr`ï¼ä»ä¸å°ä¸ç«æï¼ã - ç§»é¤è头ä¸åæ¥çâ第{unitNo}èâï¼æ¹ä¸ºå³ä¾§ `UnitNoï¼{unitNo}`ï¼åè便¬¡æ¿æ¢ä¸º `1/2/3/4`ã - è头补å `ZBåå·ï¼{zbModel}`ãæ¸²ææ¶ææ é `xNo` å®ä½äº§åç»ææ `side/SIDE` èç¹ï¼åå ¶ç¶èç¹åç§°ä½ä¸º `zbModel`ã - å¢å åèåç§°é ç½®ï¼æ¯èå¯å¨åå°ä¸å¡é ç½®ä¸åç¬è®¾ç½®åç§°ï¼Word 顶鍿 `第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ã - çæææ¡£è§£æç»æï¼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åå·ï¼åå·s44444ãä¸ä¸ï¼å¨åä¸ä¸â¦`ã - åè顶鍿 é¢å®é 为 `第1è 䏿¥äºº`ã`第2è`ã`第3è`ã`第4è`ã - æ¬è½®åç«¯éæ°æå å¹¶å¯å¨æåï¼å端æå»ºæ¤åå·²éè¿ï¼ä» ææ¢æ webpack å ä½ç§¯åè¦ã **å½åè¿è¡ç¶æï¼2026-10-09ï¼** - å端 `http://localhost:8082`ï¼PID `144352`ï¼å端 `http://127.0.0.1:8091`ï¼PID `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`ãOffice éæä»¶ `config/~$gineering-order-template.docx` æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã - `engineering-order-template.docx` å·²æ¯ v3ï¼å¦ WPS æ£å¨å ç¨è¯¥æä»¶ï¼å端å¯å¨æ¶ä¸ä¼è¦çï¼æå°æå¡ä¼ä½¿ç¨å åä¸ç 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`ï¼PID `144352`ï¼å端 `http://127.0.0.1:8091`ï¼PID `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 个å页符æ£å¸¸ã - æ ·å¼éªè¯æé´ä¸´æ¶ `8092` è¿ç¨å·²åæ¢ï¼ä¸»å端ååç«¯ä¿æè¿è¡ã **å½åè¿è¡ç¶æï¼2026-10-09ï¼** - å端 `http://localhost:8082`ï¼PID `144352`ï¼å端 `http://127.0.0.1:8091`ï¼PID `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`ãOffice éæä»¶æ `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`ï¼PID `144352`ï¼å端 `http://127.0.0.1:8091`ï¼PID `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`ï¼Word å®é ç»è®¡ `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`ï¼PID `144352`ï¼å端 `http://127.0.0.1:8091`ï¼PID `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` éè¿ï¼JDK `C:\Program Files\Java\jdk1.8.0_172`ï¼Maven `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ã - æ£å¸¸æ°æ®ç» 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`ï¼PID `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`ï¼æå°æ¥å£æµè¯ä½¿ç¨ `Authorization: Bearer demo-admin-token`ã **æ¥ææ³¨æ** - æç«¯æ åµä¸è¥åæ¡è®°å½çå®é æçé«åº¦è¶ è¿ä¸æ´é¡µï¼ä»»ä½ Word è¡çº§çº¦æé½æ æ³è®©å®å®æ´æ¾å¨ä¸é¡µï¼å½åä¸å¡æ°æ®ä¸åå¨è¯¥æ åµã - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `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` çæçæ§çæä»¶ï¼ä½åç»ç¡®è®¤è¯¥ç»è®ºä¸æç«ï¼çæ£çæ ¹å æ¯å½å代ç å¨å±å¼æç»è¡¨æ¶ï¼åªæ¿æ¢äºæ°æ®è¡ï¼æ¨¡æ¿ä¸ä½ä¸ºè头ç第 1 è¡ç¨äºç©ºå¼æ¿æ¢ï¼å¯¼è´å³ä¾§ç«æåå æ ¼çèå·/èå被åæç©ºå符串ã详è§ç¬¬ 36 èã - å½å代ç å·²æ¹ä¸ºåå¼ æç»è¡¨å æâè®°å½ 1 ç第 1-4 èãè®°å½ 2 ç第 1-4 èâ¦â¦âé¡ºåºæ¸²æï¼å¹¶ä¿ç `cantSplit + keepLines`ï¼ä¸åæå䏿 éçåèæ´ä½ `keepNext` ç»å®ï¼é¿å é¿è®°å½ææ´ç»æ¨å°ä¸ä¸é¡µã **å½å代ç éªè¯** - ç¨å䏿¹ 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` ä¹ä»ä¼ä¸èä¸é¡µã - è¿ä¸æ¯å½ååç»ä»£ç é æçï¼å½å代ç å·²æ¶é¤äºæ§çâä¸èä¸å¼ é¡¶å±è¡¨æ ¼âç硬å页ã **è¿è¡ç¶æï¼2026-10-10 13:58ï¼** - å端 `http://localhost:8082`ï¼PID `232852`ï¼ç«¯å£å·²çå¬ã - å端 `http://127.0.0.1:8091`ï¼PID `247224`ï¼å¯å¨æ¶é´ `2026-10-10 13:46:23`ï¼ç«¯å£å·²çå¬ã ## 36. ä¿®æ£å¿«ç §ï¼2026-10-10 ææå ï¼å·¥ç¨åèå·/èåæªæ¾ç¤ºä¿®å¤ï¼ **ç¨æ·åé¦** - ç¨æ·åé¦âèå·åèå页没æ¾ç¤ºâï¼å³ Word å·¥ç¨åæå³ä¾§ç«æåå æ ¼éçâ第Nè 䏿¥äººâ为空ã **æ ¹å ** - æ¨¡æ¿æç»è¡¨ç¬¬ 1 è¡æ¯è头è¡ï¼å«æå³ä¾§ `{unitDisplay}` ç«æåå æ ¼ï¼ï¼æåä¸è¡æ¯æ°æ®è¡ã - æ§é»è¾åªå±å¼å¹¶åå ¥æ°æ®è¡ï¼è头è¡ç¨ç©ºå¼æ¿æ¢ï¼å¯¼è´èå·/èå被åæç©ºå符串ï¼è¾åºéåªå©æ°æ®ã没æè头æ è¯ã **代ç ä¿®æ¹** - `EngineeringOrderPrintService.renderUnits` æ¹ä¸ºè°ç¨ `renderDetailTable(detailTable, records, unitNames)`ã - æ°å¢ `renderDetailTable`ï¼æâæ é1ç第1è³4èï¼æ é2ç第1è³4èâ¦â¦âéç»å¤å¶æ¨¡æ¿è头è¡åæ°æ®è¡ï¼å¹¶å¯¹æ¯èæ£ç¡®åå ¥ `unitNo`ã`unitName`ã`unitDisplay`ã - `unitDisplay` æ ¼å¼ä¸º `第Nè`ï¼é ç½®äºåç§°æ¶è¿½å åä¸ªç©ºæ ¼ååç§°ï¼ä¾å¦ `第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` éè¿ï¼JDK `C:\Program Files\Java\jdk1.8.0_172`ï¼Maven `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`ï¼Header 为 `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`ï¼PID `232852`ã - å端 `http://127.0.0.1:8091`ï¼PID `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`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã ## 37. ä¿®æ£å¿«ç §ï¼2026-10-10 ææå ï¼åæå°å·¥ç¨åæé»è¾é¡µè¾åºåèï¼ **ç¨æ·æ¾æ¸ ** - åâæå°å·¥ç¨åâçéå¤å使¯é»è¾é¡µï¼ä¸æ¯åæ¡è®°å½ï¼å æè®°å½å¨æå页ï¼åææ¯ä¸ªé»è¾é¡µçæ°æ®åå«è¾åºä¸ºç¬¬ 1ã2ã3ã4 èã - âæå°å·¥ç¨å2âå·²ç»ç¨³å®ï¼æ¬è½®ä¸æ¹å®ç `renderLegacyGroupedUnits()` åæåä¸å纵ååå¹¶é»è¾ã **代ç ä¿®æ¹** - `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`ï¼PID `232852`ï¼ç«¯å£å·²çå¬ã - å端 `http://127.0.0.1:8091`ï¼PID `261356`ï¼ç«¯å£å·²çå¬ï¼ç±å½å 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`ï¼PID `232852`ï¼å端 `http://127.0.0.1:8091`ï¼PID `261356`ã 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> 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")); 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; } } } 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); emergency-repair-server/src/main/java/com/emergencyrepair/business/service/EngineeringOrderPrintService.java
New file @@ -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 æå¼ä¿®æ¹æ é¢ãå宽ãè头æåï¼ä¸éè¦æ¹ä»£ç ã</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 ""; } } 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")); 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: emergency-repair-web/.env
New file @@ -0,0 +1,11 @@ # å端é ç½®ï¼ä¿®æ¹åééå¯ npm run serve / éæ° npm run build çæï¼ # å端å¼åæå¡å¨ç«¯å£ PORT=8082 # å端访é®çå端å°åï¼å¼åç¯å¢ /api 代çç®æ ï¼æ ¼å¼ http://ip:端å£ï¼ VUE_APP_API_TARGET=http://127.0.0.1:8091 # å端请æ±å端çåºç¡è·¯å¾ï¼é»è®¤ /apiï¼èµ°å¼å代çæé¨ç½²æ¶çåå代çï¼ï¼ # å¦éå端ç´è¿åç«¯å¯æ¹ä¸º http://ip:端å£/api VUE_APP_API_BASE_URL=/api emergency-repair-web/public/dict.js
@@ -5,14 +5,14 @@ window.__APP_DICT__ = { taskStatuses: { UNREPORTED: { text: 'æªä¸æ¥', type: 'info' }, REPORTED_XCB: { text: '已䏿¥rk_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' }, 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." } } 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 }) 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' } }, emergency-repair-web/src/utils/dict.js
@@ -2,10 +2,10 @@ UNREPORTED: { text: 'æªä¸æ¥', type: 'info' }, REPORTED_XCB: { text: '已䏿¥rk_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' } } 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> 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> emergency-repair-web/src/views/fault/EngineeringPrint.vue
New file @@ -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> 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="审æ¹ä¸éè¿æ¶ç´æ¥éårk_XCBå©çï¼ä»»å¡ç¶æå为âä¸çº§æåâã"></el-alert> <el-alert v-if="form.result === 'REJECT'" :closable="false" type="warning" show-icon title="审æ¹ä¸éè¿æ¶ç´æ¥éårk_第ä¸å®¡æ¹äººï¼ä»»å¡ç¶æå为âä¸çº§æåâã"></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: '对rk_XCBå©çå®¡æ ¸å®æçæ éè¿è¡æ¹ééè¿æéå', JCC_ASSISTANT: '对rk_CMBé¢å¯¼å®¡æ¹å®æçæ éè¿è¡æ¹ééè¿æéå', JCC_DIRECTOR: '对rk_JCCå©ç审æ¹å®æçæ éè¿è¡æç»å®¡æ¹ï¼éè¿åè¿å ¥æ´¾å' CMB: '对rk_第ä¸å®¡æ¹äººå®¡æ ¸å®æçæ éè¿è¡æ¹ééè¿æéå', JCC_ASSISTANT: '对rk_第äºå®¡æ¹äººå®¡æ¹å®æçæ éè¿è¡æ¹ééè¿æéå', JCC_DIRECTOR: '对rk_第ä¸å®¡æ¹äººå®¡æ¹å®æçæ éè¿è¡æç»å®¡æ¹ï¼éè¿åè¿å ¥æ´¾å' }[this.stage] }, status() { 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>åé æ¿ä¿®åä½ãè·è¸ªææ¯éåè¦æ±å®å·¥æ¥æï¼å®¡æ ¸åè¿å ¥rk_CMBé¢å¯¼å®¡æ¹</p> <h2>rk_第ä¸å®¡æ¹äººå®¡æ ¸</h2> <p>åé æ¿ä¿®åä½ãè·è¸ªææ¯éåè¦æ±å®å·¥æ¥æï¼å®¡æ ¸åè¿å ¥rk_第äºå®¡æ¹äººå®¡æ¹</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 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 } } requirement/½»½ÓÎļþ_2026.10.3.md
@@ -796,7 +796,7 @@ | é®é¢ | ç¶æ | æ ¹å ä¸ä¿®å¤ | | --- | --- | --- | | `rk_CMBé¢å¯¼å®¡æ¹` éè¿åè¿å ¥ `rk_JCCå©ç审æ¹`ï¼åè¡¨æ²¡ææ°æ® | å·²ä¿®å¤ | CMBãJCC å©çãJCC å¤é¿å ±ç¨ `SimpleApproval.vue` ååä¸ keep-alive ç¼ååï¼åç»ä»¶ä» å¨ `created()` æ¥è¯¢ä¸æ¬¡ï¼åæ¢ `stage` æ¶æ²¡æéæ°è¯·æ±ãç°çå¬ `stage`ï¼åæ¢æ¶æ¸ 空æ§å表并ç«å³ææ°ç¶ææ¥è¯¢ï¼ç¼å页ç¾åæ¬¡æ¿æ´»æ¶ä¹ä¼å·æ° | | `rk_第äºå®¡æ¹äººå®¡æ¹` éè¿åè¿å ¥ `rk_第ä¸å®¡æ¹äººå®¡æ¹`ï¼åè¡¨æ²¡ææ°æ® | å·²ä¿®å¤ | CMBãJCC å©çãJCC å¤é¿å ±ç¨ `SimpleApproval.vue` ååä¸ keep-alive ç¼ååï¼åç»ä»¶ä» å¨ `created()` æ¥è¯¢ä¸æ¬¡ï¼åæ¢ `stage` æ¶æ²¡æéæ°è¯·æ±ãç°çå¬ `stage`ï¼åæ¢æ¶æ¸ 空æ§å表并ç«å³ææ°ç¶ææ¥è¯¢ï¼ç¼å页ç¾åæ¬¡æ¿æ´»æ¶ä¹ä¼å·æ° | ### 21.1 主è¦åæ´æä»¶ requirement/¾ö²ß±í_´ýÈ·ÈÏÏî.md
@@ -23,7 +23,7 @@ > > **2026-09-16 ä¸çº§æåå£å¾**ï¼ä¸é级æåï¼CMBé¢å¯¼ãJCCå©çãJCCå¤é¿å®¡æ¹ä¸éè¿æ¶åç´æ¥éåXCBå©çï¼ä»»å¡ç¶æä¸ºâä¸çº§æåâï¼è¯¥ç¶æä¸â已䏿¥XCBâä¸å¡è¯ä¹çä»·ï¼ä½ä½ä¸ºä¸åç¶æå¼ä¿åï¼ä»¥åºåæµè½¬è½¨è¿¹ãXCBå©çæ¬ç¯èå®¡æ ¸ä¸éè¿æ¶ä¸è½¬âä¸çº§æåâï¼å 容ä¸åæ ¼ç¨âä¿®æ¹âå¤çï¼ä¸ä½ä¸ºæ éå¤çæ¶ç¹âé项âã > > **2026-09-14 ç¨æ·è¡¥å 确认**ï¼â èé¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼â¡ æ¥ä»·å¤çç§»åºä¸»æµç¨ãâæ¥ä»·ç®¡çâèå䏿¨¡åä¸åï¼â¢ JCC å©ç审æ¹ä¸ºå¿ ç»ç¯èï¼â£ 离线/æ°æ®å ç¸å ³åè½æ´ä½åæ¶ã对åºèå项ä¸å¹¶å é¤ï¼C-12 已解å³ï¼ï¼â¤ AXU2 æ°æ®å å¯¼å ¥æ éæ ¸å¯¹ï¼â¥ C-15 åå£å¾ä¸ºç»ä¸åâTåéªæ¶âï¼2026-09-20 éªæ¶æµç¨æ¹ä¸º5èååï¼è¯¥åç§°ä¸åä½ä¸ºèååï¼âTåâè§è²ä»£ç§°ä»ä¿çã > **2026-09-14 ç¨æ·è¡¥å 确认**ï¼â Té¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼â¡ æ¥ä»·å¤çç§»åºä¸»æµç¨ãâæ¥ä»·ç®¡çâèå䏿¨¡åä¸åï¼â¢ JCC å©ç审æ¹ä¸ºå¿ ç»ç¯èï¼â£ 离线/æ°æ®å ç¸å ³åè½æ´ä½åæ¶ã对åºèå项ä¸å¹¶å é¤ï¼C-12 已解å³ï¼ï¼â¤ AXU2 æ°æ®å å¯¼å ¥æ éæ ¸å¯¹ï¼â¥ C-15 åå£å¾ä¸ºç»ä¸åâTåéªæ¶âï¼2026-09-20 éªæ¶æµç¨æ¹ä¸º5èååï¼è¯¥åç§°ä¸åä½ä¸ºèååï¼âTåâè§è²ä»£ç§°ä»ä¿çã ## A. çé¢è¿åé塿 ¸å¯¹ï¼å¯¹ç § requirement\çé¢è¿å.htmlï¼AXU15 å·²äº 2026-08-26 æ ¸å¯¹å®æï¼ @@ -67,7 +67,7 @@ 5. ãå·²è§£å³ 2026-09-20ãâ鮿´¾åâ确认åºä¸ºâä¸é®æ´¾åâï¼ä¿®æ¹ãçæä¸´ä¿®å·¥ç¨åãå é¤äºå¤ï¼çä¸ºæ¸ é¤éå¤ï¼çåæªå¾ä¸å¡æé®æä¸éè¦ã 6. æ æªå¾æ¨¡å页é¢ç»æï¼çæµä¿¡æ¯ãæ¥è¯¢ï¼å·¥ç¨å/å·¥ç¨é¡¹ç®/æ éä¿¡æ¯/é项ï¼ãæ¥ä»·ç®¡çï¼å¯¼åºå¾ æ¥ä»·/ä¿®è¹åå审/JCCå®¡æ ¸ï¼ãåºç¡ä¿¡æ¯ï¼JCä¿¡æ¯/ç¨æ·æ°æ®èå´ï¼ââæ¯å¦æåèåå/çº¸è´¨ç¨¿ï¼æææèªè¡è®¾è®¡ï¼ç³»ç»ç®¡çèå已确认å 嫿¿ä¿®åä½ç®¡çã产åç»ææ 管çã设å¤åºç¡åºç®¡çï¼ç»è®¡æ¥è¡¨èå已确认6项åè½ï¼å®å·¥éªæ¶5个èå已确认ï¼ä½å段ä¸é¡µé¢ç»èå¾ è®¾è®¡ã 7. ã已解å³ãâTDâçå«ä¹ï¼2026-09-09ï¼ï¼TD=èéï¼ææè¯ä»£ç§°ï¼å·²å½å ¥ææè¯åå ¸ï¼ï¼é¦é¡µâTDâ为ç»è®¡è¡¨è¡¨å¤´åï¼âJCä¿¡æ¯âæé®=è°è¹ä¿¡æ¯å·²ç¡®è®¤ 2026-08-26ï¼ 8. ãå·²è§£å³ 2026-09-11ãèé¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼ç®åæµç¨ä» Tåæ é䏿¥ ç´æ¥è¿å ¥ XCBå©çå®¡æ ¸ 8. ãå·²è§£å³ 2026-09-11ãTé¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼ç®åæµç¨ä» Tåæ é䏿¥ ç´æ¥è¿å ¥ XCBå©çå®¡æ ¸ 9. åå ¸æ°æ®åå¼ï¼Xå·âå½åç¶æâï¼ç¤ºä¾âå°ä¿®âï¼å ¶ä½å¾ 确认ï¼ï¼æ éå½±åï¼ç¤ºä¾â便°âï¼ãæ éæ§è´¨ï¼ç¤ºä¾âé大âï¼å·²ç¡®è®¤æå¯é ç½®åå ¸åæ®µå¤çï¼å ¶ä»åå¼ä»å¾ è¡¥ï¼2026-09-16ï¼ã 10. ãAXU15 è¡¥å ãåæ®µæ¥æºä¸å¯æ¹æ§ï¼Xå·/å·¥ç¨ç¼å·/å¼å·¥æ¶é´/å®å·¥æ¶é´/æ¿ä¿®åä½çï¼åªäºç³»ç»å¸¦åºãåªäºæå¡«ãå¯å¦ä¿®æ¹ 11. ãèµæå¾ ç´¢åãå¨æç³»ç»å¯¹æ¥ææ¡£ï¼å¨æç®å½è¡¨ç»æã卿IDãæ¥å£æ¹å¼çï¼ââç¨æ·å·²æææ¡£ï¼æ¨è¿å¯¹æ¥æ¶ç´¢å requirement/ǰ44Ò³ÐèÇóÊáÀí.md
@@ -68,7 +68,7 @@ ä¸åå§éæ±çå·®å¼ï¼ - ä¸åå å«âèé¿æ¹éå®¡æ¹ / èé¿å®¡æ¹âç¯èï¼æµç¨ä» Tåæ é䏿¥ç´æ¥è¿å ¥ XCBå©çå®¡æ ¸ã - ä¸åå å«âTé¿æ¹éå®¡æ¹ / Té¿å®¡æ¹âç¯èï¼æµç¨ä» Tåæ é䏿¥ç´æ¥è¿å ¥ XCBå©çå®¡æ ¸ã - åæ¶ä¸å离线è½åï¼åæºç/ç¦»çº¿å¡«åæ°æ®å ãå ç½å¯¼å ¥å¯¼åºæ°æ®å ï¼2026-09-14 确认æ´ä½åæ¶å¹¶å é¤å¯¹åºèåé¡¹ï¼æ°æ®å å¯¼å ¥ãå®å·¥ä¿¡æ¯å¯¼å ¥ã导åºå¾ å®å·¥æ°æ®å ãæ¥ä»·ç®¡çï¼ã - åæ¶âXCB å©çå®å·¥å¤ç/导åºå®å·¥é¡¹ç®âç¯èï¼æ¹ä¸ºåå®¶ç´æ¥ä¸æ¥å®å·¥ä¿¡æ¯ã - æ¥ä»·å¤çä¸åä½ä¸ºä¸»æµç¨ç¯èï¼2026-09-14 确认ï¼âæ¥ä»·ç®¡çâèå䏿¨¡åæ´ä½ä¸åï¼ã @@ -106,8 +106,8 @@ - ãå¾ ç¡®è®¤ãéªæ¶æµç¨åæ¥éª¤çæä½è§è²ãå®å·¥éªæ¶æå°è¾åºçå®é æå°è¡ä¸ºï¼ä»¥åâä¿®åè´¨éè¯çº§âä¸ AXU14âTåè¯åâåæ®µçå ³ç³»ã - åå§æªå¾æµç¨ï¼åæ¡£ï¼å·²ä¸éç¨ï¼ï¼æ é䏿¥æ°æ®å â 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 ç»´ä¿®ä¿¡æ¯ - ãå·²è§£å³ 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 ç»´ä¿®ä¿¡æ¯ - ãå·²è§£å³ 2026-09-11ãTé¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼ç®åæµç¨ä¸å«è¯¥ç¯èï¼ ## 4. 䏿¥å®¡æ¹é¾ï¼AXU2âAXU7ï¼ @@ -292,7 +292,7 @@ 8. ãæä¸å¤ç 2026-09-20ãAXU16 ä¸çº³å ¥å½åéæ± 9. çæµä¿¡æ¯/æ¥è¯¢/æ¥ä»·/åºç¡ä¿¡æ¯ï¼æ æªå¾æ¨¡åï¼ç页é¢ç»æï¼å®å·¥éªæ¶æµç¨çèå䏿ä½å·²ç¡®è®¤ï¼é¡µé¢å段ä»å¾ 设计 10. é¦é¡µç»è®¡å¡ç宿´æ¸ åï¼JCä¿¡æ¯=è°è¹ä¿¡æ¯å·²ç¡®è®¤ 2026-08-26ï¼TD å«ä¹å¾ ç¡®è®¤ï¼ 11. ãå·²è§£å³ 2026-09-11ãâèé¿æ¹é审æ¹/èé¿å®¡æ¹âç¯èï¼ç¬¬ä¸çç®åæµç¨ä¸å«èé¿ç¯èï¼ä» Tåæ é䏿¥ç´æ¥è¿å ¥ XCBå©çå®¡æ ¸ 11. ãå·²è§£å³ 2026-09-11ãâTé¿æ¹é审æ¹/Té¿å®¡æ¹âç¯èï¼ç¬¬ä¸çç®åæµç¨ä¸å«Té¿ç¯èï¼ä» Tåæ é䏿¥ç´æ¥è¿å ¥ XCBå©çå®¡æ ¸ 12. åå ¸æ°æ®åå¼ï¼æ éå½±åï¼ç¤ºä¾â便°âï¼ãæ éæ§è´¨ï¼ç¤ºä¾âé大âï¼å为å¯é ç½®åå ¸åæ®µï¼å ¶ä»å¼å¾ è¡¥ï¼Xå·âå½åç¶æâçå ¶ä»åå ¸å¼ä¹å¾ è¡¥ 13. ãAXU15 è¡¥å 2026-09-08ãåæ®µæ¥æºä¸å¯æ¹æ§ï¼Xå·/å·¥ç¨ç¼å·/å¼å·¥æ¶é´/å®å·¥æ¶é´/æ¿ä¿®åä½çï¼åªäºç³»ç»å¸¦åºãåªäºæå¡«ãå¯å¦ä¿®æ¹ï¼å HANDOFF 第 6 è第 13 æ¡ï¼æ¬æ¬¡åæ¥å ¥åï¼ 14. ãå·²è§£å³ 2026-09-14ã离线/æ°æ®å ç¸å ³åè½æ´ä½åæ¶ï¼å¯¹åºèå项ä¸å¹¶å é¤ï¼AXU2 æ°æ®å å¯¼å ¥ï¼é¡µé¢ä¸èå项å é¤ï¼æ éæ ¸å¯¹ï¼ãå®å·¥ä¿¡æ¯å¯¼å ¥ãå·¥ç¨åæå°è¾åºçâ导åºå¾ å®å·¥æ°æ®å âãæ¥ä»·ç®¡çç导åº/å¯¼å ¥ã @@ -316,4 +316,4 @@ ## 10. å ³äºææ¡£ 45 页ä¹åç说æ - 45 页ä¹å大é¨å为â计åä¿®çâï¼AXU32âAXU66ï¼ä¸âæ¹æ¢è£ âï¼AXU67âAXU76ï¼çåå页é¢ï¼ä¸ä¸´æ¢ä¿®é¡¹ç®æ å ³ã - ææ¡£ç¬¬ 55â67 页çâ临æ¢ä¿®âè¯¦ç»æµç¨é¡µé¢ï¼AXU19âAXU31ï¼0æµç¨ç¶æ/0æµç¨è¡¨æ ¼/0.1æµç¨å¾/1æ é䏿¥/2.1èé¿æ¹é审æ¹/2.2èé¿å®¡æ¹/3.1â3.3å©ç审æ¹/4.1â4.2å¤é¿å®¡æ¹/5.1ç»´ä¿®/5.2维修信æ¯ï¼ç»ç¡®è®¤ãæä¸å¤çãã - ææ¡£ç¬¬ 55â67 页çâ临æ¢ä¿®âè¯¦ç»æµç¨é¡µé¢ï¼AXU19âAXU31ï¼0æµç¨ç¶æ/0æµç¨è¡¨æ ¼/0.1æµç¨å¾/1æ é䏿¥/2.1Té¿æ¹é审æ¹/2.2Té¿å®¡æ¹/3.1â3.3å©ç审æ¹/4.1â4.2å¤é¿å®¡æ¹/5.1ç»´ä¿®/5.2维修信æ¯ï¼ç»ç¡®è®¤ãæä¸å¤çãã requirement/¹¤³Ì״̬»úÉè¼Æ.md
@@ -1,8 +1,8 @@ # 临æ¢ä¿®è½¯ä»¶Â·å·¥ç¨ç¶ææºè®¾è®¡ï¼èæ¡ï¼ > æ¥æï¼2026-08-25 ï½ å½å便®ï¼AXU0/AXU3/AXU4/AXU10/AXU14/AXU15 åç¨æ·å·²ç¡®è®¤çæ´¾åãå®å·¥ç¶æåéªæ¶æµç¨ï¼å·¥ç¨ç¶æç»ä¸è®¾è®¡ä¸ºç¶ææº > 2026-09-11 æ´æ°ï¼æç¨æ·ç¡®è®¤çâç®åæµç¨ + åªåç½ç»çâå£å¾ä¿®è®¢ï¼æ èé¿ç¯èãæ ç¦»çº¿æ°æ®å ï¼2026-09-16 ä¿®æ£ä¸ºï¼XCBå©çå®¡æ ¸è´è´£åé æ¿ä¿®åä½ãæåãçæå·¥ç¨åå·ï¼2026-09-20 è¿ä¸æ¥ä¿®æ£ä¸ºæ´¾åé¡µææ ééæ©ãææ¿ä¿®åä½çææ´¾å·¥åï¼ã > 2026-09-14 æ´æ°ï¼ç¦»çº¿/æ°æ®å ç¸å ³åè½æ´ä½åæ¶å¹¶å é¤èåé¡¹ï¼æ°æ®å å¯¼å ¥ãå®å·¥ä¿¡æ¯å¯¼å ¥ã导åºå¾ å®å·¥æ°æ®å ãæ¥ä»·ç®¡çæ¨¡åï¼ï¼èé¿ç¯è确认ä¸çº³å ¥ç¬¬ä¸çï¼JCC å©ç审æ¹ä¸ºå¿ ç»ç¯èã > 2026-09-11 æ´æ°ï¼æç¨æ·ç¡®è®¤çâç®åæµç¨ + åªåç½ç»çâå£å¾ä¿®è®¢ï¼æ Té¿ç¯èãæ ç¦»çº¿æ°æ®å ï¼2026-09-16 ä¿®æ£ä¸ºï¼XCBå©çå®¡æ ¸è´è´£åé æ¿ä¿®åä½ãæåãçæå·¥ç¨åå·ï¼2026-09-20 è¿ä¸æ¥ä¿®æ£ä¸ºæ´¾åé¡µææ ééæ©ãææ¿ä¿®åä½çææ´¾å·¥åï¼ã > 2026-09-14 æ´æ°ï¼ç¦»çº¿/æ°æ®å ç¸å ³åè½æ´ä½åæ¶å¹¶å é¤èåé¡¹ï¼æ°æ®å å¯¼å ¥ãå®å·¥ä¿¡æ¯å¯¼å ¥ã导åºå¾ å®å·¥æ°æ®å ãæ¥ä»·ç®¡çæ¨¡åï¼ï¼Té¿ç¯è确认ä¸çº³å ¥ç¬¬ä¸çï¼JCC å©ç审æ¹ä¸ºå¿ ç»ç¯èã > 2026-09-16 æ´æ°ï¼ä»»å¡ç¶æç»ä¸ä¸ºä¸ä¸ªå¼ï¼âæªä¸æ¥ / 已䏿¥XCB / ä¸çº§æå / XCBå©çå®¡æ ¸å®æ / CMBé¢å¯¼å®¡æ¹å®æ / JCCå©ç审æ¹å®æ / JCCå¤é¿å·²é âï¼AXU4 æ¿ä¿®åä½åé ãæåãå·¥ç¨åå·çæåå¨ XCBå©çå®¡æ ¸ç¯è宿ã > 2026-09-17 æ´æ°ï¼AXU5/6/7 ä¸å¼ 审æ¹é¡µç»ä¸ä¿çå表ãå·æ°ãæ¹é审æ¹ï¼å»æä¿®æ¹ãCMBå·²é ãéé¡¹ãæ·»å è¿åº¦å¤æ³¨ï¼ç»ä¸â审æ¹âç¨è¯åâJCCå¤é¿å®¡æ¹âåç§°ï¼æ éä¿¡æ¯å¢å ä¸ç»å®¡æ¹å段ã > 2026-09-20 èå´æ¶å£ï¼AXU9ãAXU11âAXU13ãAXU16 æä¸å¤çï¼ä¸çº³å ¥å½åéæ±ï¼AXU8 ç»§ç»æç¼ã @@ -17,7 +17,7 @@ - ç¶æååè®°å½æä½äººãæä½æ¶é´ãæä½æè§ï¼å½¢æå¯è¿½æº¯çæµè½¬æ¥å¿ï¼AXU4ãAXU5/6/7 å AXU14 å·²ç¡®è®¤ä¸æä¾âæ·»å è¿åº¦å¤æ³¨âæé®ã - AXU5/6/7 䏿ä¾éç¨âä¿®æ¹/é项âæä½ï¼å®¡æ¹äººåªæ§è¡æ¹é审æ¹ï¼å®¡æ¹æè§ã审æ¹ç»æåå®¡æ¹æ¶é´éæ éä¿¡æ¯ä¿åã - æ é䏿¥é¶æ®µçé项å¨ä½ç»ä¸ç± XCBå©çæ§è¡ï¼Téåå ¶ä»å®¡æ¹è§è²ä¸æä¾é项æä½ãé项çç±ãé项人ãé项æ¶é´éæ éä¿¡æ¯ä¿åï¼é项人èªå¨åå½åç»å½ XCBå©çï¼é项æ¶é´èªå¨åç³»ç»å½åæ¶é´ã - 审æ¹ç¯è顺åºåºå®ï¼Tåæ é䏿¥âXCBå©çå®¡æ ¸âCMBé¢å¯¼å®¡æ¹âJCCå©ç审æ¹âJCCå¤é¿å®¡æ¹ï¼ï¼ä¸éè¿æµç¨å¼æå®ä¾/ä»»å¡è¡¨é©±å¨ãï¼2026-09-11 ç¨æ·ç¡®è®¤ç®åæµç¨ï¼ä¸å«èé¿ç¯èï¼ - 审æ¹ç¯è顺åºåºå®ï¼Tåæ é䏿¥âXCBå©çå®¡æ ¸âCMBé¢å¯¼å®¡æ¹âJCCå©ç审æ¹âJCCå¤é¿å®¡æ¹ï¼ï¼ä¸éè¿æµç¨å¼æå®ä¾/ä»»å¡è¡¨é©±å¨ãï¼2026-09-11 ç¨æ·ç¡®è®¤ç®åæµç¨ï¼ä¸å«Té¿ç¯èï¼ ## 1. 设计æè·¯ï¼ä¸¤å±ç¶æ requirement/¸Ä½ø2026.10.txt
File was renamed from requirement/¸Ä½ø20261003.txt @@ -43,6 +43,17 @@ 28ï¼ç»ç»æºæï¼é¨é¨æ 表ï¼ï¼å¢å æ©å±å±æ§è®¾ç½®ï¼ç®å主è¦å æ¬è´è´£rk_T 29ï¼ç¨æ·ç®¡çï¼å¢å æ©å±å±æ§è®¾ç½®ï¼ç®åä¸»è¦æå·¥ç¨ä¸ä¸ãrk_TDä¸ä¸ï¼è´è´£rk_Tï¼ç¨æ·å表å±å¼æ¾ç¤ºå·¥ç¨ä¸ä¸ãrk_Téä¸ä¸ãè´è´£rk_Tä¸åï¼æåæä½åæä¾âç¼è¾å±æ§âã 30ãæ°å¢æ éï¼é»è®¤å¼ï¼æ é䏿¥åä½é»è®¤å¡«åå½åç»å½äººçåä½ï¼è´£ä»»é»è®¤å¡«åå½åç»å½äººï¼rk_Xå·é»è®¤å¡«åå½åç»å½äººçåä½çæ©å¼ 屿§è´è´£rk_Tï¼å·¥ç¨ä¸ä¸ãrk_TDä¸ä¸åå«ä¸ºå½åç¨æ·çæ©å±å±æ§å·¥ç¨ä¸ä¸ãrk_TDä¸ä¸ 31ãæ·»å å®å·¥è¯åï¼éç¨5æè¯ååä¿®åè´¨éæè§æ 32ãå¢å å·¥ç¨åæå°ï¼æ°å¼ä¸ä¸ªèååè½ é¡µé¢ç±»åæ é䏿¥ï¼é£äºæ¥è¯¢æ¡ä»¶é½è¦ï¼ä½rk_Xå·ãrk_TDä¸ä¸æ²¡æå ¨é¨é项ï¼é»è®¤ææ¶éæ©ç¬¬ä¸ä¸ª è¦æ±ææ¨¡æ¿è¾åºå°wordï¼åææ´»çï¼{}å·éæ¯å段åï¼ ç®åå¦å¾æç¤ºï¼å¨ä¸ä¸ªwordéæå°ï¼4èï¼UnitNoï¼ï¼æå¤é6æ¡è®°å½æå°ä¸é¡µï¼ç¼å·åæ®µææ¶ä¸ç®¡ 4èï¼UnitNoï¼é£ä¸ªæ¾å¨æå³è¾¹æåè½¬åæ¾ç¤ºï¼ é£ä¸ªZBåå·ï¼{rk_Xå·çç¶èç¹ï¼å¨äº§åæ°éï¼åç§°} æ¼äº å个é ç½®æ¯1èæä¸ªåç§°ï¼ è¾åºå°wordåæ¾ç¤º å¦â第1è 䏿¥äººâ è¿æ ·çæ ¼å¼ ï¼ä¸æ¥äººæ¯ä¸ºç¬¬1èé ç½®çï¼å¯è½ä»¥åè¿æå«çå°æ¹éè¦ï¼ä½æ¯èé ç½®åè¿ä¸ªé ç½®æ¯ä¸ä¸æ · ä¸éå¶6æ¡è®°å½ï¼å¯ä»¥å¤é¡µè¾åºï¼æ¯é¡µæ¾ç¤ºé¡µä¸éå¶æ¡æ°ï¼è¦æ±è®°å½ä¸è½è·¨é¡µæ¾ç¤ºï¼ç¬¬ä¸ä¸ªè¡¨æ ¼å第33个表个åå«åºå®å¨æ¯é¡µçé¡¶é¨ååºé¨ï¼ 33ãå端端å£ãå端访é®å端ipå端å£åæé ç½® 34ã requirement/Ãô¸Ð´Ê×Öµä.md
@@ -29,6 +29,6 @@ - ææ¡£ä¸æææ¶å以ä¸å«ä¹å¤ä¸å¾ä½¿ç¨**代称å**åç§°ï¼è¯æ ¹ä»£ç§°ï¼ç»åè¯ç´æ¥ä»¥ä»£ç§°ä¹¦åï¼ã - æ¯è¯ç»ä¸ï¼2026-09-09ï¼ï¼çé¢/ææ¡£ä¸å¾å代称ï¼å¦ Xå·=è·å·ï¼ï¼ä¸ä½¿ç¨è¿ä¼¼çå®è¡¨è¿°ï¼å¦âè°å·âï¼ï¼TD=èé ä½ä¸ºç¬ç«è¯æ¡ç»´æ¤ã - æ¯è¯æ´æ°ï¼2026-09-20ï¼ï¼2026-09-14 æ¾ç»ä¸åâTåéªæ¶âï¼ç°å®å·¥éªæ¶æµç¨åºå®ä¸º5个èåï¼éªæ¶ç³è¯·/è´¨æ£åéªæ¶/è´¨æ£è´è´£äººéªæ¶/å®å·¥éªæ¶æå°è¾åº/ä¿®åè´¨éè¯çº§ï¼ï¼ä¸å使ç¨âTåéªæ¶âä½ä¸ºèååãTå=èåï¼ç± T è¯æ ¹æ´¾çï¼è¯åä¸è§è²åä»ç»ä¸åâTåâã - æ¯è¯æ´æ°ï¼2026-09-20ï¼ï¼2026-09-14 æ¾ç»ä¸åâTåéªæ¶âï¼ç°å®å·¥éªæ¶æµç¨åºå®ä¸º5个èåï¼éªæ¶ç³è¯·/è´¨æ£åéªæ¶/è´¨æ£è´è´£äººéªæ¶/å®å·¥éªæ¶æå°è¾åº/ä¿®åè´¨éè¯çº§ï¼ï¼ä¸å使ç¨âTåéªæ¶âä½ä¸ºèååãTå=Tåï¼ç± T è¯æ ¹æ´¾çï¼è¯åä¸è§è²åä»ç»ä¸åâTåâã - æ°å¢ææè¯ç±ç¨æ·æä¾å追å å°ç¬¬ 2 èè¡¨æ ¼å JSON é ç½®ã - ç产ç¯å¢æ¿æ¢é»è¾ï¼ä» æ¿æ¢ `real_name` ä¸ `codename` ä¸å䏿ªä½¿ç¨å ä½ç¬¦çè¯æ¡ï¼åå ¸ä¸å¯ç¨æ¶ç»§ç»è¾åºå¼å代称ã requirement/½çÃæ»¹Ô.html
@@ -18,7 +18,7 @@ </style></head><body> <h1>临æ¢ä¿®è½¯ä»¶ · çé¢è¯å«è¿åï¼åå§éæ±.docx å 44 页ï¼</h1> <p style="font-size:13px;color:#555">ç¨éï¼ä¸åå§ææ¡£éå¼ æ ¸å¯¹ OCR/è¯å«ç»æãé»è²é«äº® = å¾ ç¡®è®¤/OCRç误ãè¡¨æ ¼å为横åæåï¼ä¸ä»£è¡¨åå¾å®é å顺åºã</p> <div class="note">å ¨å±å£å¾ï¼2026-09-11 ç¨æ·ç¡®è®¤ï¼ï¼ç¬¬ä¸ç<mark>䏿åå§éæ±</mark>å®ç°ï¼æ¹ä¸º â <b>ç®åä¸å¡æµç¨</b> + â¡ <b>åªæä¾ç½ç»çã䏿ä¾ç¦»çº¿åæºç</b>ï¼ç®åæµç¨è§ AXU1 å¡çãææâåæºçå¡«å/离线填å/æ°æ®å å¯¼å ¥å¯¼åºâç¸å ³åè½åä¸è¿å ¥ç¬¬ä¸çã<mark>2026-09-20 ç¨æ·ç¡®è®¤ï¼JCCå¤é¿å®¡æ¹å®æåç´æ¥è¿å ¥XCBå©çæ´¾å页é¢ï¼é¡µé¢æ¾ç¤ºæªæ´¾åæ éï¼å¾éåç¹å»âä¸é®æ´¾åâï¼ææ¿ä¿®åä½çææ´¾å·¥åï¼ç¼å·ä¸ºæ éç¼å· + `/01`ã`/02` çãé¤âä¸é®æ´¾åâå¤ï¼å AXU10 å ¶ä»ä¸å¡æé®æä¸éè¦ãæ éç¼å·ç±ç³»ç»æ `åºå®åç¼-Xå·/åç±»-YYMMDD-nnn` èªå¨çæï¼æ éä¿¡æ¯ä¿çâæ éç¼å·âåæ®µï¼2026-09-28 æ´æ°ï¼ã</mark><mark>2026-09-14 ç¨æ·ç¡®è®¤ï¼ç¦»çº¿/æ°æ®å ç¸å ³åè½æ´ä½åæ¶ã对åºèå项ä¸å¹¶å é¤ï¼C-12 已解å³ï¼ï¼âæ¥ä»·ç®¡çâèå䏿¨¡åæ´ä½ä¸åï¼æ¥ä»·å¤çç§»åºä¸»æµç¨ï¼ï¼èé¿ç¯èä¸çº³å ¥ç¬¬ä¸çï¼JCC å©ç审æ¹ä¸ºå¿ ç»ç¯èï¼AXU2 æ°æ®å å¯¼å ¥æ éæ ¸å¯¹ã</mark></div> <div class="note">å ¨å±å£å¾ï¼2026-09-11 ç¨æ·ç¡®è®¤ï¼ï¼ç¬¬ä¸ç<mark>䏿åå§éæ±</mark>å®ç°ï¼æ¹ä¸º â <b>ç®åä¸å¡æµç¨</b> + â¡ <b>åªæä¾ç½ç»çã䏿ä¾ç¦»çº¿åæºç</b>ï¼ç®åæµç¨è§ AXU1 å¡çãææâåæºçå¡«å/离线填å/æ°æ®å å¯¼å ¥å¯¼åºâç¸å ³åè½åä¸è¿å ¥ç¬¬ä¸çã<mark>2026-09-20 ç¨æ·ç¡®è®¤ï¼JCCå¤é¿å®¡æ¹å®æåç´æ¥è¿å ¥XCBå©çæ´¾å页é¢ï¼é¡µé¢æ¾ç¤ºæªæ´¾åæ éï¼å¾éåç¹å»âä¸é®æ´¾åâï¼ææ¿ä¿®åä½çææ´¾å·¥åï¼ç¼å·ä¸ºæ éç¼å· + `/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ãAXU11ãAXU12ãAXU13ãAXU16 æä¸å¤çï¼ä¸çº³å ¥å½åéæ±ä¸åç»æ ¸å¯¹ï¼AXU8 ç»§ç»æç¼ã</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é¢å¯¼ãJCCå©çãJCCå¤é¿å®¡æ¹ä¸éè¿æ¶ï¼åç´æ¥éåXCBå©çï¼ä»»å¡ç¶æä¸ºâä¸çº§æåâï¼ä¸é级æåãâä¸çº§æåâä¸â已䏿¥XCBâä¸å¡è¯ä¹çä»·ï¼ä½ä½ä¸ºä¸åç¶æå¼ä¿åï¼ä»¥åºåæµè½¬è½¨è¿¹ã</div> <div class="label">ç¸å¯¹åå§éæ±çå·®å¼ï¼2026-09-11 确认ï¼</div> <div class="note2">â ä¸åå å«âèé¿æ¹é审æ¹/èé¿å®¡æ¹âï¼ç´æ¥ä» Tåæ é䏿¥å¼å§ï¼â¡ åæ¶âåæºçå¡«å/ç¦»çº¿å¡«åæ°æ®å + å ç½å¯¼å ¥/å¯¼åºæ°æ®å âï¼â¢ åæ¶âXCBå©çå®å·¥å¤çï¼å¯¼åºå®å·¥é¡¹ç®/åæºçå¡«å/å家离线填åï¼âç¯èï¼æ¹ä¸ºåå®¶ç´æ¥ä¸æ¥å®å·¥ä¿¡æ¯ï¼â£ æ¥ä»·å¤çä¸åä½ä¸ºä¸»æµç¨ç¯èï¼âæ¥ä»·ç®¡çâè忝å¦ä¿çå¾ ç¡®è®¤ï¼ï¼â¤ JCCå©ç审æ¹ç¡®è®¤ä¸ºæµç¨å¿ ç»ç¯èï¼åæªå¾æµç¨å¾æªååï¼ã</div> <div class="note2">â ä¸åå å«âTé¿æ¹é审æ¹/Té¿å®¡æ¹âï¼ç´æ¥ä» Tåæ é䏿¥å¼å§ï¼â¡ åæ¶âåæºçå¡«å/ç¦»çº¿å¡«åæ°æ®å + å ç½å¯¼å ¥/å¯¼åºæ°æ®å âï¼â¢ åæ¶âXCBå©çå®å·¥å¤çï¼å¯¼åºå®å·¥é¡¹ç®/åæºçå¡«å/å家离线填åï¼âç¯èï¼æ¹ä¸ºåå®¶ç´æ¥ä¸æ¥å®å·¥ä¿¡æ¯ï¼â£ æ¥ä»·å¤çä¸åä½ä¸ºä¸»æµç¨ç¯èï¼âæ¥ä»·ç®¡çâè忝å¦ä¿çå¾ ç¡®è®¤ï¼ï¼â¤ JCCå©ç审æ¹ç¡®è®¤ä¸ºæµç¨å¿ ç»ç¯èï¼åæªå¾æµç¨å¾æªååï¼ã</div> <div class="note2">åå§æªå¾æµç¨ï¼åæ¡£åèï¼å·²ä¸éç¨ï¼ï¼æ é䏿¥æ°æ®å â XCBå©çå®¡æ ¸ï¼å¯ç´æ¥é项ï¼â CMBé¢å¯¼å®¡æ¹ï¼å¯æåä¸ä¸æ¥ï¼â JCCå¤é¿å®¡æ¹ â XCBå©çæ´¾åï¼æå°æ´¾å·¥åï¼åXå·+å䏿¥æ¥æ+åæ¿ä¿®åä½åæä¸ä¸ªæ´¾å·¥åï¼â å家线ä¸ä¿®ç â XCBå©çå®å·¥å¤çï¼å¯¼åºå®å·¥é¡¹ç®/åæºçå¡«å/å家离线填å/å ç½ä¸æ¥ï¼â æå°å®å·¥éªæ¶åï¼Tåéªæ¶ â æ¥ä»·å¤çï¼å¯¼åºå¾ æ¥ä»·é¡¹ç® â åæºçå¡«å/å¯¼å ¥ â ä¿®è¹åå©ç审价 â JCC审价ï¼ã</div> <div class="note2">æ¯è¯ï¼2026-09-14 ç¨æ·ç¡®è®¤ï¼ï¼ç»ä¸åâTåâï¼T=è代称ï¼Tå=èåï¼ï¼åææ¡£èåçæ§åæ³å·²ä½åºï¼è¯åä¸è§è²åä¸å¹¶ç»ä¸ä¸ºâTåâã</div> <div class="note2">æ¯è¯ï¼2026-09-14 ç¨æ·ç¡®è®¤ï¼ï¼ç»ä¸åâTåâï¼T=è代称ï¼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 · æ é䏿¥ï¼ææ¡£é¡µ 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">æ¯å¦æ¶H</span><span class="td">çæµé¡¹ç®</span><span class="td">çæµæ°å¼</span><span class="td">è¦æ±å®å·¥æ¥æ</span><span class="td">夿³¨</span></div><div class="note2">ä»»å¡ç¶æï¼2026-09-16 确认ï¼å ¨å±ä¸ä¸ªå¼ï¼ï¼æªä¸æ¥ / 已䏿¥XCB / ä¸çº§æå / 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é¢å¯¼å®¡æ¹æè§ãCMBé¢å¯¼å®¡æ¹ç»æï¼éè¿/ä¸éè¿ï¼ãCMBé¢å¯¼å®¡æ¹æ¶é´ï¼JCCå©çå®¡æ¹æè§ãJCCå©ç审æ¹ç»æï¼éè¿/ä¸éè¿ï¼ãJCCå©çå®¡æ¹æ¶é´ï¼JCCå¤é¿å®¡æ¹æè§ãJCCå¤é¿å®¡æ¹ç»æï¼éè¿/ä¸éè¿ï¼ãJCCå¤é¿å®¡æ¹æ¶é´ã</div><div class="note2">é项忮µç»ï¼2026-09-17 确认ï¼ï¼é项çç±ãé项人ãé项æ¶é´ï¼æ é䏿¥é¶æ®µçé项ç»ä¸ç±XCBå©çæ§è¡ï¼Téåå ¶ä»å®¡æ¹è§è²ä¸æä¾é项æä½ãé项人èªå¨åå½åç»å½XCBå©çï¼é项æ¶é´èªå¨åç³»ç»å½åæ¶é´ã</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">æ¯å¦æ¶H</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 å£å¾ï¼ï¼ç±ç³»ç»èªå¨çæï¼è§å <b>åºå®åç¼-Xå·/åç±»-YYMMDD-nnn</b>ï¼Xå·å3使°åï¼åç±»åâTéä¸ä¸é ç½®âç A/Bï¼YYMMDD ä¸ºä¸æ¥æ¥æï¼nnn 为åä¸ Xå·+åä¸Téä¸ä¸+åä¸ä¸æ¥æ¥æçåºå·ï¼ä» 001 èµ·ï¼ã表åä¸è¯¥å段åªè¯»ï¼æ¾ç¤ºâä¿ååèªå¨çæâã</div><div class="label">æ°å¢è¡¨å交äºï¼2026-09-16 确认ï¼ï¼</div><div class="note2">ç¹å»âæ°å¢âå¼¹åºè¡¨åï¼ä¸ä½¿ç¨åéæ±çè¡¨æ ¼å¼å½å ¥ã</div><div class="note2">é»è®¤å¼ï¼ä¸æ¥åä½=ç»å½ç¨æ·æå±åä½ï¼Téä¸ä¸=ç»å½ç¨æ·æå±ä¸ä¸ï¼è´è´£äºº=ç»å½ç¨æ·ï¼ä¸æ¥æ¥æ=å½åæ¥æã</div><div class="note2">䏿忮µï¼Téä¸ä¸ãå·¥ç¨ä¸ä¸ãæ éå½±åãæ éæ§è´¨ã</div><div class="note2">ç¹å»âè£ å¤åç§°âæå¼äº§åç»ææ ï¼éæ©è£ å¤ï¼è®¾å¤ï¼åï¼èªå¨å¡«å ¥âè£ å¤åç§°âåâæå±ä¸çº§ç³»ç»âã</div><div class="note2">H 为ææè¯åå ¸ä»£ç§°ï¼å段ç»ä¸åâæ¯å¦æ¶Hâã</div></section><section class="card"><h3>AXU4 · XCBå©çå®¡æ ¸ï¼ææ¡£é¡µ 10ï¼</h3><div class="note">页é¢å®ä½ï¼ç®åæµç¨ç¬¬ 2 æ¥âXCBå©çå®¡æ ¸âã</div><div class="note">å½åå£å¾ï¼2026-09-16 确认ï¼ï¼é¡µé¢åèååç§°ç»ä¸åâXCBå©çå®¡æ ¸âï¼é¡µé¢å åâ审æ¹âææ¡ç»ä¸æ¹ä¸ºâå®¡æ ¸âã</div><div class="note2">ã已确认 2026-09-16ãæ¬ç¯è宿ï¼åé æ¿ä¿®åä½ãææ¿ä¿®å使åãçæå·¥ç¨åå·ï¼XCBå©çå®¡æ ¸å®æåå³å¯é¢å¨æãJCCå¤é¿å®¡æ¹å®æåç´æ¥è¿å ¥XCBå©çæ´¾å页é¢ï¼é¡µé¢æ¾ç¤ºæªæ´¾åæ éï¼å¾éåä¸é®æ´¾åå¹¶ææ¿ä¿®åä½çææ´¾å·¥åï¼ä¸ååé æä¿®æ¹æ¿ä¿®åä½ã</div><div class="label">ä¸å¡è§å</div><div class="note2">ä¸ä¸ªæ éåå¯ä»¥åé å¤ä¸ªæ¿ä¿®åä½ï¼å®¡æ ¸å®æåææ¿ä¿®å使æå¤ä¸ªå·¥ç¨åèµ°åç»æµç¨ã</div><div class="note2">å·¥ç¨åå·å¨æ¬ç¯èçæï¼è§åï¼åºå®-Xå·/çç»ç±»å«-YYMMDD-å家代å·/åºå·ï¼å ¶ä¸ 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">é项ï¼ä» XCBå©çï¼</span><span class="td">å®¡æ ¸éè¿/ä¸éè¿</span><span class="td"><mark>å·²å»æï¼æ·»å è¿åº¦å¤æ³¨ãCMBå·²é </mark></span></div><div class="note2">ä»»å¡ç¶æç»ä¸éç¨å ¨å±ä¸ä¸ªå¼ï¼æªä¸æ¥ / 已䏿¥XCB / ä¸çº§æå / 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">æ¯å¦æ¶H</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">æ¯å¦æ¶H</span><span class="td">æ¯å¦çæµé¡¹ç®</span><span class="td">é大æ éæè¿°</span><span class="td">çæµæ°æ®å¼</span><span class="td">夿³¨</span></div><div class="note2">å®¡æ ¸è¡¨åå å«éè¿ãä¸éè¿ãå ³éãXCBå©çå®¡æ ¸ä¸éè¿æ¶ä¸è½¬âä¸çº§æåâï¼å 容ä¸åæ ¼ç±XCBå©çéè¿âä¿®æ¹âæé®èªè¡ä¿®æ£ï¼ä¸ä½ä¸ºæ éå¤çæ¶ç¹âé项âã</div><div class="note2">é项æéä¸å段ï¼2026-09-17 确认ï¼ï¼æ é䏿¥é¶æ®µçé项ç»ä¸ç±XCBå©çæ§è¡ï¼è®°å½é项çç±ãé项人ãé项æ¶é´ãé项人èªå¨åå½åç»å½XCBå©çï¼é项æ¶é´èªå¨åç³»ç»å½åæ¶é´ã</div></section><section class="card"><h3>AXU5 · CMBé¢å¯¼å®¡æ¹ï¼ææ¡£é¡µ 11ï¼</h3><div class="note">å表 + æ¹é审æ¹</div><div class="note2">AXU5/6/7 å ±éï¼2026-09-17 确认ï¼ï¼ä¿çå表ãå·æ°ãæ¹é审æ¹ï¼å»æä¿®æ¹ãCMBå·²é ãéé¡¹ãæ·»å è¿åº¦å¤æ³¨ï¼ä¸é¡µç»ä¸ä½¿ç¨â审æ¹âç¨è¯ã</div><div class="label">æé®/æä½</div><div class="chips"><span class="td">å表</span><span class="td">å·æ°</span><span class="td">æ¹é审æ¹</span><span class="td"><mark>已廿ï¼ä¿®æ¹ãCMBå·²é ãéé¡¹ãæ·»å è¿åº¦å¤æ³¨</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">æ¯å¦æ¶H</span><span class="td">çæµé¡¹ç®</span><span class="td">çæµæ°å¼</span><span class="td">è¦æ±å®å·¥æ¥æ</span><span class="td">夿³¨</span></div><div class="note2">ä»»å¡ç¶æï¼ç»ä¸éç¨å ¨å±ä¸ä¸ªå¼ï¼æ¬é¡µå®¡æ¹ä¸éè¿æ¶ç´æ¥è½¬ä¸ºâä¸çº§æåâå¹¶éåXCBå©çã</div><div class="note2">æ éä¿¡æ¯å®¡æ¹å段ç»ï¼2026-09-17 确认ï¼ï¼CMBé¢å¯¼å®¡æ¹æè§ãCMBé¢å¯¼å®¡æ¹ç»æï¼éè¿/ä¸éè¿ï¼ãCMBé¢å¯¼å®¡æ¹æ¶é´ï¼JCCå©çå®¡æ¹æè§ãJCCå©ç审æ¹ç»æï¼éè¿/ä¸éè¿ï¼ãJCCå©çå®¡æ¹æ¶é´ï¼JCCå¤é¿å®¡æ¹æè§ãJCCå¤é¿å®¡æ¹ç»æï¼éè¿/ä¸éè¿ï¼ãJCCå¤é¿å®¡æ¹æ¶é´ã</div><div class="label">审æ¹è¡¨åï¼</div><div class="chips"><span class="td">å®¡æ¹æè§</span><span class="td">å ³é / éè¿ / ä¸éè¿</span></div></section><section class="card"><h3>AXU6 · JCCå©ç审æ¹ï¼ææ¡£é¡µ 12ï¼</h3><div class="note">ä¸ AXU5 å ±ç¨åä¸å¥çé¢ååè½ï¼2026-09-17 确认ï¼</div><div class="note2">ä¸çº§æåè§åï¼JCCå©ç审æ¹ä¸éè¿æ¶ç´æ¥éåXCBå©çï¼ä»»å¡ç¶æè½¬ä¸ºâä¸çº§æåâï¼ä¸é级æåã</div><div class="label">æé®/æä½</div><div class="chips"><span class="td">å表</span><span class="td">å·æ°</span><span class="td">æ¹é审æ¹</span><span class="td"><mark>已廿ï¼ä¿®æ¹ãCMBå·²é ãéé¡¹ãæ·»å è¿åº¦å¤æ³¨</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">æ¯å¦æ¶H</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å¤é¿å®¡æ¹ï¼ææ¡£é¡µ 13ï¼</h3><div class="note">ä¸ AXU5/6 å ±ç¨åä¸å¥çé¢ååè½ï¼2026-09-17 确认ï¼ï¼é¡µé¢åèååç§°ç»ä¸ä¸ºâJCCå¤é¿å®¡æ¹â</div><div class="note2">ä¸çº§æåè§åï¼JCCå¤é¿å®¡æ¹ä¸éè¿æ¶ç´æ¥éåXCBå©çï¼ä»»å¡ç¶æè½¬ä¸ºâä¸çº§æåâï¼ä¸é级æåã</div><div class="label">æé®/æä½</div><div class="chips"><span class="td">å表</span><span class="td">å·æ°</span><span class="td">æ¹é审æ¹</span><span class="td"><mark>已廿ï¼ä¿®æ¹ãCMBå·²é ãéé¡¹ãæ·»å è¿åº¦å¤æ³¨</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">æ¯å¦æ¶H</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>已确认为工ç¨ç¶æçéå ¥å£ï¼æé®/页ç¾ï¼ï¼å½¢å¼å¾ å®</mark></span></div><div class="note2">䏿¹ç¤ºææ§è¡¨æ ¼ï¼è¡¨å¤´ä» Column1/Column2/Column3 + 2 è¡ç©ºè¡ï¼ä½è æªå¡«å段ï¼ââæç¶æçéå±ç¤ºå·¥ç¨å表ï¼å段åç»èªè¡è®¾è®¡</div></section><section class="card"><h3>AXU9 · CMBé¢å¯¼å·²é ï¼ææ¡£é¡µ 15ï¼</h3><div class="note"><mark>æä¸å¤ç 2026-09-20ï¼ä¸çº³å ¥å½åéæ±ä¸åç»æ ¸å¯¹ã</mark></div><div class="note">页é¢è¯´æï¼âæ¥çCMBé¢å¯¼å·²æ¹é çâå·¥ç¨</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">æ¯å¦æ¶H</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å¤é¿å®¡æ¹å®æåç´æ¥è¿å ¥XCBå©çæ´¾åï¼AXU9ä¸ä½ä¸ºå¿ ç»æµç¨èç¹ã</div></section><section class="card"><h3>AXU10 · æ´¾å管çï¼ææ¡£é¡µ 16ï¼2026-09-20 æç¨æ·å£å¾éå®ä¹ï¼</h3> <div class="note2">页é¢å ¥å£ï¼JCCå¤é¿å®¡æ¹å®æåï¼æµç¨ç´æ¥è¿å ¥ XCBå©çæ´¾å页é¢ã</div> <div class="note2">页é¢å å®¹ï¼æ¾ç¤ºâæªæ´¾åâçæ éä¸è§ã</div>