feat: complete system management and repair workflow updates
- add user, role, menu button, organization, login log, and operation log management
- switch product tree and contractor data sources
- complete fault numbering, audit fields, logical deletion, and repair flow fixes
- document requirements 1-19 and the 2026-10-04 integration handoff
| | |
| | | |
| | | ### 0.10 2026-09-28 ä¸ä¸é
ç½®ä¸æ
éç¼å·èªå¨çæï¼æ¬æ¬¡ä¼è¯ï¼å·²å®ç°å¹¶éªè¯ï¼ |
| | | |
| | | ç¨æ·æ¬æ¬¡ç»åºç 4 æ¡æ°éæ±ï¼â ç³»ç»ç®¡çå¢å âå·¥ç¨ä¸ä¸é
ç½®âï¼å段ä»
åç§°ï¼ï¼â¡ ç³»ç»ç®¡çå¢å âTéä¸ä¸é
ç½®âï¼å段åç§°ãåç±» A/Bï¼ï¼â¢ æ
éç¼å·ä¸åæå·¥è¾å
¥ï¼æ¹ä¸ºæ `æ½1åº-Xå·/A-YYMMDD-nnn` èªå¨çæï¼â£ æ´¾åå·ä¸º `æ
éç¼å·/nn`ï¼æ¿ä¿®åä½åºå·ä» 01 èµ·ã |
| | | ç¨æ·æ¬æ¬¡ç»åºç 4 æ¡æ°éæ±ï¼â ç³»ç»ç®¡çå¢å âå·¥ç¨ä¸ä¸é
ç½®âï¼å段ä»
åç§°ï¼ï¼â¡ ç³»ç»ç®¡çå¢å âTéä¸ä¸é
ç½®âï¼å段åç§°ãåç±» A/Bï¼ï¼â¢ æ
éç¼å·ä¸åæå·¥è¾å
¥ï¼æ¹ä¸ºæ `åºå®åç¼-Xå·/A-YYMMDD-nnn` èªå¨çæï¼â£ æ´¾åå·ä¸º `æ
éç¼å·/nn`ï¼æ¿ä¿®åä½åºå·ä» 01 èµ·ã |
| | | |
| | | å·²å®ç°ï¼ |
| | | - âå·¥ç¨ä¸ä¸é
ç½®âï¼èå `/system/engineering-majors`ï¼å段ä»
âåç§°âï¼è¡¨ `LX.T_ENGINEERING_MAJOR(ID, NAME, CREATE_TIME, UPDATE_TIME)`ï¼æ¥å£ `/api/system/engineering-majors`ï¼GET/POST/PUT/DELETEï¼ã |
| | | - âTéä¸ä¸é
ç½®âï¼èå `/system/team-majors`ï¼å段âåç§°ãåç±»ï¼A/Bï¼âï¼è¡¨ `LX.T_TEAM_MAJOR(ID, NAME, CATEGORY, CREATE_TIME, UPDATE_TIME)`ï¼æ¥å£ `/api/system/team-majors`ï¼GET/POST/PUT/DELETEï¼ã |
| | | - 两å¤é
ç½®åæ ¡éªåç§°é空ä¸éåï¼Téä¸ä¸åç±»åªå
许 A/Bãå¯å¨æ¶ `DataInitializer.ensureMajorConfigMenus` å¹çè¡¥èåï¼`seedMajors` é¢ç½®æ¼ç¤ºæ°æ®ï¼å·¥ç¨ä¸ä¸ å¨å/çµæ°/æ¶²å/è¾
æºï¼Téä¸ä¸ å¨å=Aãæ¶²å=Aãçµæ°=Bãè¾
æº=Bï¼é»è®¤æåï¼å¯å¨é¡µé¢ä¿®æ¹ï¼ã |
| | | - æ
éç¼å·èªå¨çæï¼`BusinessService.generateFaultCode`ï¼æ ¼å¼ `æ½1åº-Xå·/åç±»-YYMMDD-nnn`ã |
| | | - åç¼åºå® `æ½1åº-`ï¼Xå·å 3 使°åï¼`X01` â `001`ï¼ã |
| | | - æ
éç¼å·èªå¨çæï¼`BusinessService.generateFaultCode`ï¼æ ¼å¼ `åºå®åç¼-Xå·/åç±»-YYMMDD-nnn`ã |
| | | - åç¼åºå® `åºå®åç¼-`ï¼Xå·å 3 使°åï¼`X01` â `001`ï¼ã |
| | | - åç±»åâTéä¸ä¸é
ç½®âç A/Bï¼æªé
ç½®æåç±»éæ³æ¶ä¿åæ¥éå¹¶æç¤ºå
è¡ç»´æ¤ã |
| | | - YYMMDD å䏿¥æ¥æï¼nnn 为âåä¸ Xå· + åä¸ Téä¸ä¸ + åä¸ä¸æ¥æ¥æâçåºå·ï¼ä» 001 èµ·ã |
| | | - æ°å»ºèªå¨çæï¼ä¿®æ¹æ¶å¼ºå¶æ²¿ç¨åç¼å·ï¼æ¹ Téä¸ä¸/æ¥æä¹ä¸éæ°çæï¼ã |
| | |
| | | éªè¯ï¼2026-09-28ï¼ï¼ |
| | | - å端 `mvn -q -DskipTests package` éè¿ï¼å端 `vue-cli-service build` éè¿ï¼ä»
æ¢æå
ä½ç§¯æç¤ºï¼ã |
| | | - æ¥å£ï¼å·¥ç¨ä¸ä¸ãTéä¸ä¸åè¿å 4 æ¡ï¼`/api/system/menu/nav` ç³»ç»ç®¡ç䏿°å¢ä¸¤é¡¹ï¼æåº 35/36ï¼ã |
| | | - èªå¨ç¼å·ï¼`X02 + å¨åä¸ä¸(A) + 2026-09-28` â `æ½1åº-002/A-260928-001`ã`æ½1åº-002/A-260928-002`ï¼æ¢ Téä¸ä¸ï¼çµæ° Bï¼â `æ½1åº-002/B-260928-001`ï¼æ¢ä¸æ¥æ¥æ â `æ½1åº-001/A-260929-001`ï¼ä¿®æ¹æ
éæ¹ Téä¸ä¸åç¼å·ä¿æä¸åã |
| | | - æ´¾å端å°ç«¯ï¼ä¸æ¡æ
éåé
两个æ¿ä¿®åä½ï¼çæ `æ½1åº-002/B-260928-001/01`ã`.../02`ãæµè¯æ°æ®å·²ä» `LX.T_FAULT`ã`LX.T_WORK_ORDER` æ¸
çã |
| | | - èªå¨ç¼å·ï¼`X02 + å¨åä¸ä¸(A) + 2026-09-28` â `åºå®åç¼-002/A-260928-001`ã`åºå®åç¼-002/A-260928-002`ï¼æ¢ Téä¸ä¸ï¼çµæ° Bï¼â `åºå®åç¼-002/B-260928-001`ï¼æ¢ä¸æ¥æ¥æ â `åºå®åç¼-001/A-260929-001`ï¼ä¿®æ¹æ
éæ¹ Téä¸ä¸åç¼å·ä¿æä¸åã |
| | | - æ´¾å端å°ç«¯ï¼ä¸æ¡æ
éåé
两个æ¿ä¿®åä½ï¼çæ `åºå®åç¼-002/B-260928-001/01`ã`.../02`ãæµè¯æ°æ®å·²ä» `LX.T_FAULT`ã`LX.T_WORK_ORDER` æ¸
çã |
| | | - æµè§å¨ï¼ä¸¤ä¸ªæ°é
置页æ£å¸¸æ¸²æï¼æ
é䏿¥æ°å¢è¡¨åâæ
éç¼å·âåªè¯»ä¸å ä½âä¿ååèªå¨çæâï¼Téä¸ä¸/å·¥ç¨ä¸ä¸ä¸æä¸ºé
置项ï¼ä¿®å¤åæ§å¶å°æ æ°å¢æ¥éã |
| | | - å岿¼ç¤ºæ
éä»ä½¿ç¨ `GZ-2026-0xx` æ§ç¼å·ï¼æªè¿ç§»ï¼é¿å
å½±å已派工åå¼ç¨ï¼ï¼æ°å»ºæ
éä¸å¾ä½¿ç¨æ°è§åã |
| | | - ææ¡£å·²åæ¥ï¼`requirement\å³ç表_å¾
确认项.md`ã`å44é¡µéæ±æ¢³ç.md`ã`å·¥ç¨ç¶ææºè®¾è®¡.md`ã`çé¢è¿å.html`ï¼ä»¥åé¦é¡µå
¬åâæ´¾åè§åâææ¡ã |
| | |
| | | |
| | | **æ¬æ¬¡ä¼è¯å®æï¼ç»èè§ 0.10 / 0.11ï¼** |
| | | - å³ç表 B1âB4 æå»ºè®®å
³éå¹¶è½å°ï¼ç³»ç»ç®¡çæ°å¢âå·¥ç¨ä¸ä¸é
ç½®âï¼å段ï¼åç§°ï¼ãâTéä¸ä¸é
ç½®âï¼å段ï¼åç§°ãåç±» A/Bï¼ã |
| | | - æ
éç¼å·æ¹ä¸ºèªå¨çæï¼`æ½1åº-Xå·/A-YYMMDD-nnn`ï¼Xå· 3 使°åï¼Aï¼Téä¸ä¸ A/Bï¼nnn 为åä¸ Xå·ï¼åä¸ä¸ä¸ï¼åä¸ä¸æ¥æ¥æå
ä» 001 éå¢ï¼ï¼æ´¾åå·ï¼`æ
éç¼å·/nn`ï¼æ¿ä¿®åä½åºå·ï¼ä» 01 èµ·ï¼ã |
| | | - æ
éç¼å·æ¹ä¸ºèªå¨çæï¼`åºå®åç¼-Xå·/A-YYMMDD-nnn`ï¼Xå· 3 使°åï¼Aï¼Téä¸ä¸ A/Bï¼nnn 为åä¸ Xå·ï¼åä¸ä¸ä¸ï¼åä¸ä¸æ¥æ¥æå
ä» 001 éå¢ï¼ï¼æ´¾åå·ï¼`æ
éç¼å·/nn`ï¼æ¿ä¿®åä½åºå·ï¼ä» 01 èµ·ï¼ã |
| | | - æ
é䏿¥é¡µï¼ä»»å¡ç¶ææ ç¾ä¸ä»»å¡ç¶æä¸ææ¡ææä¸¤è¡ï¼æ ç¾æ¢å°ä¸ä¸è¡ï¼ã |
| | | - å®å·¥ä¿¡æ¯ä¸æ¥é¡µï¼AXU15ï¼ï¼ä¿®åè´¨éæ
åµç¬å æ´è¡ï¼é¡¹ç®åç§°ä¸éªæ¶æ¹å¼åä¸è¡ï¼æ
é说æãåå åæåç¬å æ´è¡ï¼âå¤çè§£å³æ
åµâæ¹ä¸ºå¯å¤è¡çè¡¨æ ¼ï¼åºå·ï¼å¤çè§£å³æ
åµï¼ï¼ç»´ä¿®å¨æè¡¨åæ¹ä¸ºåºå·ãåç§°ãè§æ ¼åå·ãææ¯åæ°ãæ°éã计éåä½ãåæ ¼è¯æãåºåºåå·ãæ¥æºã夿³¨ãå端æ°å¢ `LX.T_COMPLETION_INFO.SOLUTIONS_JSON` ä¸ `MaterialItem.certificate`ï¼å¹¶ç»§ç»å
¼å®¹å `SOLUTION` åæ®µã |
| | | - 项ç®çº³å
¥å
¬å¸ Git ä»åºï¼`http://admin@61.183.254.94:3000/r/EmergencyRepair.git`ï¼åæ¯ `main`ï¼é¦æäº¤ `5305262`ï¼103 个æä»¶ï¼ï¼éå `0bacdcf` 补记è¿ç«¯ä¿¡æ¯ã |
| | |
| | | 2. `sensitive.js` æ¹å¨å·²è·ç¡®è®¤å¹¶éæ¬æ¬¡æäº¤æ¨éï¼`requirement\~$åå§éæ±.docx` æ¯ Word éæä»¶ï¼ä¸è¦æäº¤ã |
| | | 3. 鿱侧å¾
确认项ä¸åï¼å³ç表 B6âB10ãAXU15 åæ®µæ¥æºä¸å¯æ¹æ§ãå®å·¥éªæ¶åç¯èè§è²ä¸æå°è¾åºãä¿®åè´¨éè¯çº§ä¸ AXU14 Tåè¯ååæ®µçå
³ç³»ã |
| | | 4. åç»æ¹å¨å®æåæ¨éå°å
¬å¸ Git ä»åº `main` 忝ã |
| | | |
| | | ## 17. æ¶å°¾å¿«ç
§ï¼2026-10-04 å¨å
ï¼éæ± 11 / 12 / 17-19 èè°æ¶å°¾ï¼ |
| | | |
| | | **æ¬è½®å®æå
容** |
| | | - éæ± 11ï¼å®å·¥æ
åµå¤çå°ç¶ææ¹ä¸ºâå®å·¥âæ¶ï¼åªæ´æ°æ´¾å·¥åç¶æï¼ä¸å跳转âå®å·¥ä¿¡æ¯å¡«æ¥âãå®å·¥å¤çä¸å®å·¥å¡«æ¥ç±ä¸åç¨æ·å嫿ä½ã |
| | | - éæ± 12ï¼å®å·¥ä¿¡æ¯å¡«æ¥ç派工åéæ©å表åªå±ç¤ºâå·²å¨å®å·¥æ
åµå¤çä¸ç¡®è®¤å®å·¥ãå±äºå½åç»å½ç¨æ·æå±åä½ãä¸å°æªå¡«æ¥âçæ´¾å·¥åï¼å¹¶å¢å å·¥ç¨åç§°ï¼é¡¹ç®åç§°ï¼æ¾ç¤ºã |
| | | - éæ± 17ï¼è§è²ç®¡ççâç¨æ·è®¾ç½®âå¢å âå
¨é¨ / 已设置âè¿æ»¤åæ¢ã |
| | | - éæ± 18ï¼æ°å¢âç»ç»æºæâé¨é¨æ 表页é¢ï¼èå `/system/organization`ï¼é¡µé¢ `OrganizationManage.vue`ï¼æ°æ®æ¥èª `CSICZB_ZB.SYS_DEPT`ï¼å端æ¥å£ `/api/system/depts`ãå½åç¯å¢è¿å 34 个é¨é¨ï¼æ¯æåç§°ãç¼ç ãè´è´£äººãé¨é¨ç±»åçéã |
| | | - éæ± 19ï¼æ¿ä¿®å使¹ä¸ºè¯»å `SYS_DEPT.DEPT_TYPE='cj'` çé¨é¨è®°å½ï¼é¨é¨ ID ä½ä¸ºæ¿ä¿®åä½ IDï¼æ¿ä¿®å使¥å£ä¿ç GETï¼é¡µé¢æ¹ä¸ºåªè¯»ãå½åç¯å¢è¿å 7 个 cj é¨é¨ï¼XCBå®¡æ ¸ãæ´¾ååä½¿ç¨ cj é¨é¨ ID/åç§°ã |
| | | |
| | | **ä¸»è¦æä»¶** |
| | | - å端ï¼`emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java`ã`emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java`ã`emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java`ã |
| | | - å端ï¼`emergency-repair-web/src/views/system/OrganizationManage.vue`ã`emergency-repair-web/src/views/system/ContractorManage.vue`ã`emergency-repair-web/src/views/completion/CompletionReport.vue`ã`emergency-repair-web/src/views/system/RoleManage.vue`ã`emergency-repair-web/src/router/index.js`ã |
| | | |
| | | **éªè¯ç»æ** |
| | | - å端 `mvn -B -DskipTests clean package` éè¿ï¼å端 `npm run build` éè¿ã |
| | | - 页é¢éªè¯èæ¬å®æ´éè¿ï¼`node .codex-run/verify-items-11-12-17.js`ã`node .codex-run/verify-items-18-19.js`ï¼æªå¾è§ `.codex-run/organization-verified.png`ã`.codex-run/contractor-readonly-verified.png`ã`.codex-run/completion-report-verified.png`ã`.codex-run/role-scope-verified.png`ã |
| | | - 端å°ç«¯èè°ä½¿ç¨ä¸´æ¶æ
é `åºå®åç¼-088/A-261004-001`ï¼è·é䏿¥ãXCBå®¡æ ¸åé
âåå®¶1/åå®¶2âãCMB/JCCä¸çº§å®¡æ¹ãæ´¾åãæç»æ
éä¿å `contractorIds = 1303144990487465986,1303145150793764866`ã`contractorNames = åå®¶1ãåå®¶2`ï¼çæä¸¤å¼ 派工åï¼åä¿å cj é¨é¨ ID/åç§°ã |
| | | - å®å·¥å¡«æ¥å¯è§æ§éªè¯ï¼`cjyh1-1` ä»
çå°åå®¶1派工åï¼`cjyh2-1` ä»
çå°åå®¶2派工åï¼`admin` å¯çå°ä¸¤å¼ ãä¸´æ¶ `T_FAULT` / `T_WORK_ORDER` æµè¯æ°æ®å·²æ¸
çã |
| | | |
| | | **å½åè¿è¡ç¶æï¼2026-10-04ï¼** |
| | | - å端ï¼`http://localhost:8082`ï¼å端ï¼`http://127.0.0.1:8091`ï¼DM8ï¼`127.0.0.1:5236`ï¼headless Edge è°è¯å®ä¾ç«¯å£ `9222`ã |
| | | - ç»å½ï¼`admin / admin123`ï¼æ°æ®åºï¼`SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | |
| | | **æ¥ææ³¨æäºé¡¹** |
| | | - é管çåç¨æ·çå®å·¥å¡«æ¥æ´¾å·¥ååä½å¹é
å½åæ `DEPT_NAME`ï¼ä¸æ¯æ `DEPT_ID`ãæ¬æ¬¡éªè¯éè¿ï¼å¦åå¨ååé¨é¨ï¼åºæ¹ä¸ºæé¨é¨ ID 精确å¹é
ã |
| | | - å岿¼ç¤ºæ´¾å·¥åå¯è½ä¿çæ§ `T_CONTRACTOR` åç§°ï¼é管çå cj ç¨æ·å¯è½çä¸å°è¿äºåå²åï¼`admin` ä¸ååä½è¿æ»¤å½±åã |
| | | - `.codex-run`ãOffice éæä»¶ `requirement\~$åå§éæ±.docx` å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` 屿¬å°èè°æä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | - æ¬è½®ä»£ç ä¸äº¤æ¥ææ¡£æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ï¼åç»å¯å¨ãæå»ºåæéå½ä»¤æ²¿ç¨ç¬¬ 16 èã |
| | |
| | | <artifactId>spring-boot-starter-validation</artifactId> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>org.springframework.security</groupId> |
| | | <artifactId>spring-security-crypto</artifactId> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>com.baomidou</groupId> |
| | | <artifactId>mybatis-plus-boot-starter</artifactId> |
| | | <version>${mybatis-plus.version}</version> |
| | |
| | | package com.emergencyrepair.auth; |
| | | |
| | | import com.emergencyrepair.common.Result; |
| | | import com.emergencyrepair.system.entity.SysLoginLog; |
| | | import com.emergencyrepair.system.mapper.SysLoginLogMapper; |
| | | import com.emergencyrepair.system.service.SystemService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.dao.DataAccessException; |
| | | import org.springframework.jdbc.core.JdbcTemplate; |
| | | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestHeader; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.time.LocalDateTime; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | @RequiredArgsConstructor |
| | | public class AuthController { |
| | | private final JdbcTemplate jdbcTemplate; |
| | | private final SysLoginLogMapper loginLogMapper; |
| | | private final SystemService systemService; |
| | | private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); |
| | | |
| | | @PostMapping("/login") |
| | | public Result<Map<String, Object>> login(@RequestBody Map<String, String> body) { |
| | | String username = body.get("username"); |
| | | String password = body.get("password"); |
| | | if (!"admin".equals(username) || (!"admin".equals(password) && !"admin123".equals(password))) { |
| | | return Result.error("ç¨æ·åæå¯ç é误ï¼ç¬¬ä¸çè¯·ä½¿ç¨ admin/admin123"); |
| | | public Result<Map<String, Object>> login(@RequestBody Map<String, String> body, HttpServletRequest request) { |
| | | String username = body == null || body.get("username") == null ? "" : body.get("username").trim(); |
| | | String password = body == null || body.get("password") == null ? "" : body.get("password"); |
| | | Map<String, Object> user = findUser(username); |
| | | boolean success = false; |
| | | String message = "ç¨æ·åæå¯ç é误"; |
| | | if (user != null) { |
| | | if (isDisabled(user)) { |
| | | message = "è´¦å·å·²åç¨"; |
| | | } else if (matches(password, stringValue(user.get("password")))) { |
| | | success = true; |
| | | } else if ("admin".equals(username) && ("admin".equals(password) || "admin123".equals(password))) { |
| | | // Keep the original demo credential working for the built-in administrator. |
| | | success = true; |
| | | } |
| | | } |
| | | recordLogin(username, user, request, success, success ? "ç»å½æå" : message); |
| | | if (!success) { |
| | | return Result.error(message); |
| | | } |
| | | Map<String, Object> data = new LinkedHashMap<>(); |
| | | data.put("token", "demo-admin-token"); |
| | | data.put("username", "admin"); |
| | | data.put("realName", "ç³»ç»ç®¡çå"); |
| | | data.put("deptName", resolveDepartmentName(username)); |
| | | data.put("token", "demo-" + username + "-token"); |
| | | data.put("username", username); |
| | | data.put("realName", StringUtils.hasText(stringValue(user.get("nickName"))) |
| | | ? stringValue(user.get("nickName")) : username); |
| | | data.put("deptName", stringValue(user.get("deptName"))); |
| | | data.put("roles", systemService.roleCodes(username)); |
| | | data.put("permissions", systemService.permissionCodes(username)); |
| | | return Result.ok(data); |
| | | } |
| | | |
| | | @GetMapping("/me") |
| | | public Result<Map<String, Object>> me() { |
| | | public Result<Map<String, Object>> me(@RequestHeader(value = "X-Username", required = false) String headerUser, |
| | | @RequestHeader(value = "Authorization", required = false) String authorization) { |
| | | String username = currentUsername(headerUser, authorization); |
| | | Map<String, Object> user = findUser(username); |
| | | if (user == null) { |
| | | return Result.error("ç»å½ç¶æå·²å¤±æ"); |
| | | } |
| | | Map<String, Object> data = new LinkedHashMap<>(); |
| | | data.put("username", "admin"); |
| | | data.put("realName", "ç³»ç»ç®¡çå"); |
| | | data.put("deptName", resolveDepartmentName("admin")); |
| | | data.put("roles", new String[]{"SYSTEM_ADMIN"}); |
| | | data.put("username", username); |
| | | data.put("realName", StringUtils.hasText(stringValue(user.get("nickName"))) |
| | | ? stringValue(user.get("nickName")) : username); |
| | | data.put("deptName", stringValue(user.get("deptName"))); |
| | | data.put("roles", systemService.roleCodes(username)); |
| | | data.put("permissions", systemService.permissionCodes(username)); |
| | | return Result.ok(data); |
| | | } |
| | | |
| | | private String resolveDepartmentName(String username) { |
| | | private Map<String, Object> findUser(String username) { |
| | | if (!StringUtils.hasText(username)) { |
| | | return ""; |
| | | return null; |
| | | } |
| | | try { |
| | | List<String> names = jdbcTemplate.query( |
| | | "SELECT d.DEPT_NAME FROM LX.SYS_USER u LEFT JOIN LX.SYS_DEPT d ON d.DEPT_ID = u.DEPT_ID WHERE u.USER_NAME = ? AND u.DEL_FLAG = '0'", |
| | | List<Map<String, Object>> rows = jdbcTemplate.query( |
| | | "SELECT u.USER_ID, u.USER_NAME, u.NICK_NAME, u.PASSWORD, u.STATUS, d.DEPT_NAME " + |
| | | "FROM LX.SYS_USER u LEFT JOIN LX.SYS_DEPT d ON d.DEPT_ID = u.DEPT_ID " + |
| | | "WHERE u.USER_NAME = ? AND (u.DEL_FLAG = '0' OR u.DEL_FLAG IS NULL)", |
| | | new Object[]{username}, |
| | | (rs, rowNum) -> rs.getString(1)); |
| | | return names.isEmpty() || names.get(0) == null ? "" : names.get(0).trim(); |
| | | (rs, rowNum) -> { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | row.put("userId", rs.getLong("USER_ID")); |
| | | row.put("userName", rs.getString("USER_NAME")); |
| | | row.put("nickName", rs.getString("NICK_NAME")); |
| | | row.put("password", rs.getString("PASSWORD")); |
| | | row.put("status", rs.getString("STATUS")); |
| | | row.put("deptName", rs.getString("DEPT_NAME")); |
| | | return row; |
| | | }); |
| | | return rows.isEmpty() ? null : rows.get(0); |
| | | } catch (DataAccessException ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private boolean matches(String rawPassword, String encodedPassword) { |
| | | if (!StringUtils.hasText(rawPassword) || !StringUtils.hasText(encodedPassword)) { |
| | | return false; |
| | | } |
| | | try { |
| | | return passwordEncoder.matches(rawPassword, encodedPassword.trim()); |
| | | } catch (Exception ex) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private boolean isDisabled(Map<String, Object> user) { |
| | | return "1".equals(stringValue(user.get("status"))); |
| | | } |
| | | |
| | | private String stringValue(Object value) { |
| | | return value == null ? "" : String.valueOf(value).trim(); |
| | | } |
| | | |
| | | private String currentUsername(String headerUser, String authorization) { |
| | | if (StringUtils.hasText(headerUser)) { |
| | | return headerUser.trim(); |
| | | } |
| | | if (!StringUtils.hasText(authorization)) { |
| | | return ""; |
| | | } |
| | | String token = authorization.trim(); |
| | | if (token.startsWith("Bearer ")) { |
| | | token = token.substring(7).trim(); |
| | | } |
| | | if (token.startsWith("demo-") && token.endsWith("-token")) { |
| | | return token.substring(5, token.length() - 6); |
| | | } |
| | | return token; |
| | | } |
| | | |
| | | private void recordLogin(String username, Map<String, Object> user, HttpServletRequest request, |
| | | boolean success, String message) { |
| | | try { |
| | | SysLoginLog log = new SysLoginLog(); |
| | | log.setUserId(user == null ? null : (Long) user.get("userId")); |
| | | log.setUserName(username); |
| | | log.setNickName(user == null ? null : stringValue(user.get("nickName"))); |
| | | log.setDeptName(user == null ? "" : stringValue(user.get("deptName"))); |
| | | log.setLoginIp(clientIp(request)); |
| | | log.setStatus(success ? "SUCCESS" : "FAIL"); |
| | | log.setMessage(message); |
| | | log.setLoginTime(LocalDateTime.now()); |
| | | loginLogMapper.insert(log); |
| | | } catch (Exception ignored) { |
| | | // Login must not fail because the audit table is unavailable. |
| | | } |
| | | } |
| | | |
| | | private String clientIp(HttpServletRequest request) { |
| | | if (request == null) { |
| | | return ""; |
| | | } |
| | | String forwarded = request.getHeader("X-Forwarded-For"); |
| | | if (StringUtils.hasText(forwarded)) { |
| | | return forwarded.split(",")[0].trim(); |
| | | } |
| | | return request.getRemoteAddr(); |
| | | } |
| | | } |
| | |
| | | import com.emergencyrepair.business.entity.Acceptance; |
| | | import com.emergencyrepair.business.entity.BaseEquipment; |
| | | import com.emergencyrepair.business.entity.CompletionInfo; |
| | | import com.emergencyrepair.business.entity.Contractor; |
| | | import com.emergencyrepair.business.entity.EngineeringMajor; |
| | | import com.emergencyrepair.business.entity.Fault; |
| | | import com.emergencyrepair.business.entity.ProductNode; |
| | |
| | | import com.emergencyrepair.business.mapper.AcceptanceMapper; |
| | | import com.emergencyrepair.business.mapper.BaseEquipmentMapper; |
| | | import com.emergencyrepair.business.mapper.CompletionInfoMapper; |
| | | import com.emergencyrepair.business.mapper.ContractorMapper; |
| | | import com.emergencyrepair.business.mapper.EngineeringMajorMapper; |
| | | import com.emergencyrepair.business.mapper.FaultMapper; |
| | | import com.emergencyrepair.business.mapper.ProductNodeMapper; |
| | |
| | | import com.emergencyrepair.system.mapper.SysRoleMenuMapper; |
| | | import com.emergencyrepair.system.mapper.SysRoleUserMapper; |
| | | import com.emergencyrepair.system.mapper.SysTenantMenuMapper; |
| | | import com.emergencyrepair.system.service.ButtonPermissionCatalog; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.boot.CommandLineRunner; |
| | | import org.springframework.core.annotation.Order; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.jdbc.core.JdbcTemplate; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | |
| | | private final SysRoleMenuMapper roleMenuMapper; |
| | | private final SysTenantMenuMapper tenantMenuMapper; |
| | | private final SysRoleUserMapper roleUserMapper; |
| | | private final ContractorMapper contractorMapper; |
| | | private final EngineeringMajorMapper engineeringMajorMapper; |
| | | private final TeamMajorMapper teamMajorMapper; |
| | | private final CompletionInfoMapper completionInfoMapper; |
| | |
| | | private final AcceptanceMapper acceptanceMapper; |
| | | private final BusinessService businessService; |
| | | private final ObjectMapper objectMapper; |
| | | private final JdbcTemplate jdbcTemplate; |
| | | private final ButtonPermissionCatalog buttonPermissionCatalog; |
| | | |
| | | private final Map<String, Long> menuIds = new HashMap<>(); |
| | | private final Map<String, Long> baseIds = new HashMap<>(); |
| | | private final Map<String, Long> productDeviceIds = new HashMap<>(); |
| | | private final List<Long> contractorIds = new ArrayList<>(); |
| | | private final Map<Long, String> contractorNames = new HashMap<>(); |
| | | |
| | | @Override |
| | | @Transactional |
| | | public void run(String... args) { |
| | | seedMenusAndRoles(); |
| | | ensureSystemMenus(); |
| | | normalizeSensitiveTerms(); |
| | | normalizeEquipmentTerms(); |
| | | seedButtonMenus(); |
| | | ensureAdminRoleAssignment(); |
| | | ensureMajorConfigMenus(); |
| | | seedMajors(); |
| | | seedContractors(); |
| | | seedTrees(); |
| | | indexProductDevices(); |
| | | ensureDemoProductDevices(); |
| | | indexProductDevices(); |
| | | backfillFaultXNoFromEquipment(); |
| | | linkFaultEquipment(); |
| | |
| | | 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, "XCBå©çå®¡æ ¸", "/fault/review", null, 1, 20); |
| | | createMenu("cmb-approval", fault, "CMBé¢å¯¼å®¡æ¹", "/fault/approval/cmb", null, 1, 30); |
| | | createMenu("jcc-assistant", fault, "JCCå©ç审æ¹", "/fault/approval/jcc-assistant", null, 1, 40); |
| | | createMenu("jcc-director", fault, "JCCå¤é¿å®¡æ¹", "/fault/approval/jcc-director", null, 1, 50); |
| | | createMenu("dispatch", fault, "XCBå©çæ´¾å", "/fault/dispatch", null, 1, 60); |
| | | 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("completion-list", fault, "å®å·¥æ
åµå¤ç", "/fault/completion", null, 1, 70); |
| | | createMenu("completion-report", fault, "å®å·¥ä¿¡æ¯ä¸æ¥", "/completion/report", null, 1, 80); |
| | | |
| | |
| | | createMenu("accept-rating", acceptance, "ä¿®åè´¨éè¯çº§", "/acceptance/rating", null, 1, 50); |
| | | |
| | | Long reports = createMenu("reports", null, "ç»è®¡æ¥è¡¨", null, "el-icon-data-analysis", 0, 40); |
| | | createMenu("report-overview", reports, "è£
夿
éæ»ä½åæ", "/reports/overview", null, 1, 10); |
| | | createMenu("report-overview", reports, "è®¾å¤æ
éæ»ä½åæ", "/reports/overview", null, 1, 10); |
| | | createMenu("report-top", reports, "æ
éé«å设å¤Top", "/reports/top-equipment", null, 1, 20); |
| | | createMenu("report-team", reports, "æTç»è®¡", "/reports/by-team", null, 1, 30); |
| | | createMenu("report-team", reports, "rk_æTç»è®¡", "/reports/by-team", null, 1, 30); |
| | | createMenu("report-contractor", reports, "åå®¶ç»´ä¿®æ
åµ", "/reports/contractors", null, 1, 40); |
| | | createMenu("report-trend", reports, "æ
éæ°/å¤åå¤ä»¶æ¶è年度è¶å¿", "/reports/trend", null, 1, 50); |
| | | |
| | | Long system = createMenu("system", null, "ç³»ç»ç®¡ç", null, "el-icon-setting", 0, 50); |
| | | createMenu("organization", system, "ç»ç»æºæ", "/system/organization", null, 1, 5); |
| | | createMenu("contractors", system, "æ¿ä¿®åä½ç®¡ç", "/system/contractors", null, 1, 10); |
| | | createMenu("product-tree", system, "产åç»ææ 管ç", "/system/product-tree", null, 1, 20); |
| | | createMenu("base-equipment", system, "设å¤åºç¡åºç®¡ç", "/system/base-equipment", null, 1, 30); |
| | | createMenu("engineering-majors", system, "å·¥ç¨ä¸ä¸é
ç½®", "/system/engineering-majors", null, 1, 35); |
| | | createMenu("team-majors", system, "Téä¸ä¸é
ç½®", "/system/team-majors", null, 1, 36); |
| | | createMenu("team-majors", system, "rk_Téä¸ä¸é
ç½®", "/system/team-majors", null, 1, 36); |
| | | createMenu("roles", system, "è§è²ç®¡ç", "/system/roles", null, 1, 40); |
| | | createMenu("menus", system, "èå管ç", "/system/menus", null, 1, 50); |
| | | createMenu("menu-permissions", system, "èåæé管ç", "/system/menu-permissions", null, 1, 60); |
| | |
| | | .last("LIMIT 1")); |
| | | Long pid = system == null ? 0L : system.getId(); |
| | | ensureMenu(pid, "å·¥ç¨ä¸ä¸é
ç½®", "/system/engineering-majors", 35); |
| | | ensureMenu(pid, "Téä¸ä¸é
ç½®", "/system/team-majors", 36); |
| | | ensureMenu(pid, "rk_Téä¸ä¸é
ç½®", "/system/team-majors", 36); |
| | | } |
| | | |
| | | private void ensureMenu(Long pid, String name, String url, int sort) { |
| | |
| | | menu.setCreateTime(LocalDateTime.now()); |
| | | menu.setUpdateTime(LocalDateTime.now()); |
| | | menuMapper.insert(menu); |
| | | } |
| | | |
| | | private void ensureSystemMenus() { |
| | | SysMenu system = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getName, "\u7cfb\u7edf\u7ba1\u7406").last("LIMIT 1")); |
| | | Long pid = system == null ? 0L : system.getId(); |
| | | ensureMenu(pid, "ç»ç»æºæ", "/system/organization", 5); |
| | | ensureMenu(pid, "\u7528\u6237\u7ba1\u7406", "/system/users", 37); |
| | | ensureMenu(pid, "\u767b\u5f55\u65e5\u5fd7", "/system/login-logs", 65); |
| | | ensureMenu(pid, "\u64cd\u4f5c\u65e5\u5fd7", "/system/operation-logs", 66); |
| | | grantAdminMenus("/system/organization", "/system/users", "/system/login-logs", "/system/operation-logs"); |
| | | } |
| | | |
| | | private void grantAdminMenus(String... urls) { |
| | | SysRole adminRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery() |
| | | .eq(SysRole::getCode, "SYS_ADMIN").last("LIMIT 1")); |
| | | if (adminRole == null) { |
| | | return; |
| | | } |
| | | for (String url : urls) { |
| | | SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getUrl, url).last("LIMIT 1")); |
| | | if (menu != null) { |
| | | ensureRoleMenu(adminRole.getId(), menu.getId()); |
| | | ensureTenantMenu(1L, menu.getId()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Materializes the declarative button catalog as MENU_TYPE=2 rows. |
| | | * Existing rows are left untouched so manual menu edits are preserved. |
| | | */ |
| | | private void seedButtonMenus() { |
| | | SysRole adminRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery() |
| | | .eq(SysRole::getCode, "SYS_ADMIN").last("LIMIT 1")); |
| | | for (Map.Entry<String, List<ButtonPermissionCatalog.ButtonDefinition>> entry : buttonPermissionCatalog.catalog().entrySet()) { |
| | | SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getUrl, entry.getKey()).last("LIMIT 1")); |
| | | if (menu == null || menu.getMenuType() == null || menu.getMenuType() != 1) { |
| | | continue; |
| | | } |
| | | List<ButtonPermissionCatalog.ButtonDefinition> definitions = entry.getValue(); |
| | | for (int index = 0; index < definitions.size(); index++) { |
| | | ButtonPermissionCatalog.ButtonDefinition definition = definitions.get(index); |
| | | SysMenu existing = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getPid, menu.getId()) |
| | | .eq(SysMenu::getMenuType, 2) |
| | | .eq(SysMenu::getPermissions, definition.getCode()) |
| | | .last("LIMIT 1")); |
| | | if (existing != null) { |
| | | continue; |
| | | } |
| | | SysMenu child = new SysMenu(); |
| | | child.setPid(menu.getId()); |
| | | child.setName(definition.getName()); |
| | | child.setPermissions(definition.getCode()); |
| | | child.setMenuType(2); |
| | | child.setSortNo((index + 1) * 10); |
| | | child.setVisible(1); |
| | | child.setCreateTime(LocalDateTime.now()); |
| | | child.setUpdateTime(LocalDateTime.now()); |
| | | menuMapper.insert(child); |
| | | if (adminRole != null) { |
| | | ensureRoleMenu(adminRole.getId(), child.getId()); |
| | | } |
| | | ensureTenantMenu(1L, child.getId()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void ensureRoleMenu(Long roleId, Long menuId) { |
| | | if (roleId == null || menuId == null) { |
| | | return; |
| | | } |
| | | long count = roleMenuMapper.selectCount(Wrappers.<SysRoleMenu>lambdaQuery() |
| | | .eq(SysRoleMenu::getRoleId, roleId).eq(SysRoleMenu::getMenuId, menuId)); |
| | | if (count == 0) { |
| | | SysRoleMenu relation = new SysRoleMenu(); |
| | | relation.setRoleId(roleId); |
| | | relation.setMenuId(menuId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | roleMenuMapper.insert(relation); |
| | | } |
| | | } |
| | | |
| | | private void ensureTenantMenu(Long tenantId, Long menuId) { |
| | | if (tenantId == null || menuId == null) { |
| | | return; |
| | | } |
| | | long count = tenantMenuMapper.selectCount(Wrappers.<SysTenantMenu>lambdaQuery() |
| | | .eq(SysTenantMenu::getTenantId, tenantId).eq(SysTenantMenu::getMenuId, menuId)); |
| | | if (count == 0) { |
| | | SysTenantMenu relation = new SysTenantMenu(); |
| | | relation.setTenantId(tenantId); |
| | | relation.setMenuId(menuId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | tenantMenuMapper.insert(relation); |
| | | } |
| | | } |
| | | |
| | | private void ensureAdminRoleAssignment() { |
| | | SysRole adminRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery() |
| | | .eq(SysRole::getCode, "SYS_ADMIN").last("LIMIT 1")); |
| | | if (adminRole == null) { |
| | | return; |
| | | } |
| | | List<Long> ids = jdbcTemplate.query( |
| | | "SELECT USER_ID FROM CSICZB_ZB.SYS_USER WHERE USER_NAME = 'admin' AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL)", |
| | | (rs, rowNum) -> rs.getLong(1)); |
| | | if (!ids.isEmpty()) { |
| | | Long userId = ids.get(0); |
| | | long count = roleUserMapper.selectCount(Wrappers.<SysRoleUser>lambdaQuery() |
| | | .eq(SysRoleUser::getRoleId, adminRole.getId()) |
| | | .eq(SysRoleUser::getUserId, userId)); |
| | | if (count == 0) { |
| | | SysRoleUser relation = new SysRoleUser(); |
| | | relation.setRoleId(adminRole.getId()); |
| | | relation.setUserId(userId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | roleUserMapper.insert(relation); |
| | | } |
| | | } |
| | | Integer placeholder = jdbcTemplate.queryForObject( |
| | | "SELECT COUNT(*) FROM CSICZB_ZB.SYS_USER WHERE USER_ID = 1", Integer.class); |
| | | if (placeholder == null || placeholder == 0) { |
| | | roleUserMapper.delete(Wrappers.<SysRoleUser>lambdaQuery().eq(SysRoleUser::getUserId, 1L)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | private void seedContractors() { |
| | | if (contractorMapper.selectCount(null) > 0) { |
| | | return; |
| | | } |
| | | String[][] rows = { |
| | | {"åæ¹æºçµä¿®çå", "åæ¹æºçµ", "åå®¶", "1", "å¼ å·¥", "0"}, |
| | | {"æµ·åå¨å设å¤å", "æµ·åå¨å", "åå®¶", "2", "æå·¥", "0"}, |
| | | {"èåçµæ°ç»´ä¿®ä¸å¿", "èåçµæ°", "åå®¶", "3", "çå·¥", "0"}, |
| | | {"åè¿æºæ¢°ä¿®é å", "åè¿æºæ¢°", "åå®¶", "4", "赵工", "0"}, |
| | | {"æ±æµ·èªå¨åæå¡é¨", "æ±æµ·èªå¨å", "æå¡å", "5", "å¨å·¥", "0"}, |
| | | {"è¿èªç²æ¿è®¾å¤å
¬å¸", "è¿èªç²æ¿", "åå®¶", "6", "éå·¥", "0"}, |
| | | {"精工液åç»´ä¿®å", "精工液å", "åå®¶", "7", "åå·¥", "0"}, |
| | | {"坿æ§å¶è®¾å¤å", "坿æ§å¶", "åå®¶", "8", "åå·¥", "0"}, |
| | | {"æä¿¡ç®¡ç³»å·¥ç¨é¨", "æä¿¡ç®¡ç³»", "æå¡å", "9", "å´å·¥", "1"}, |
| | | {"æ°å综åç»´ä¿®ä¸å¿", "æ°åç»´ä¿®", "综ååä½", "10", "éå·¥", "0"} |
| | | }; |
| | | for (String[] row : rows) { |
| | | Contractor contractor = new Contractor(); |
| | | contractor.setName(row[0]); |
| | | contractor.setShortName(row[1]); |
| | | contractor.setNature(row[2]); |
| | | contractor.setSortNo(Integer.valueOf(row[3])); |
| | | contractor.setContact(row[4]); |
| | | contractor.setDishonest(Integer.valueOf(row[5])); |
| | | contractor.setCreateTime(LocalDateTime.now()); |
| | | contractor.setUpdateTime(LocalDateTime.now()); |
| | | contractorMapper.insert(contractor); |
| | | contractorIds.add(contractor.getId()); |
| | | contractorIds.clear(); |
| | | contractorNames.clear(); |
| | | List<Map<String, Object>> rows = jdbcTemplate.query( |
| | | "SELECT DEPT_ID, DEPT_NAME FROM CSICZB_ZB.SYS_DEPT " + |
| | | "WHERE DEPT_TYPE = 'cj' AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL) ORDER BY ORDER_NUM, DEPT_ID", |
| | | (rs, rowNum) -> { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("id", rs.getLong("DEPT_ID")); |
| | | item.put("name", rs.getString("DEPT_NAME")); |
| | | return item; |
| | | }); |
| | | for (Map<String, Object> item : rows) { |
| | | Long id = ((Number) item.get("id")).longValue(); |
| | | contractorIds.add(id); |
| | | contractorNames.put(id, String.valueOf(item.get("name"))); |
| | | } |
| | | } |
| | | |
| | | private void seedTrees() { |
| | | if (productNodeMapper.selectCount(null) == 0) { |
| | | ProductNode model = product("LX-Aå综åä¿éå¹³å°", "LX-A", null, 1, "MODEL", null, 1); |
| | | ProductNode x01 = product("X01", "X01", model.getId(), 2, "X_NO", null, 1); |
| | | ProductNode x02 = product("X02", "X02", model.getId(), 2, "X_NO", null, 2); |
| | | ProductNode x03 = product("X03", "X03", model.getId(), 2, "X_NO", null, 3); |
| | | if (baseEquipmentMapper.selectCount(null) == 0) { |
| | | Long power = base("å¨åç³»ç»", "BASE-POWER", null, 1, 1); |
| | | Long electric = base("çµæ°ç³»ç»", "BASE-ELECTRIC", null, 1, 2); |
| | | Long deck = base("ç²æ¿ç³»ç»", "BASE-DECK", null, 1, 3); |
| | |
| | | Long controlDevice = base("é䏿§å¶æ", "DEV-CONTROL", control, 3, 1); |
| | | Long hydraulicDevice = base("æ¶²åæ³µç»", "DEV-HYD", hydraulic, 3, 1); |
| | | Long compressorDevice = base("空æ°å缩æºç»", "DEV-AIR", auxiliary, 3, 1); |
| | | |
| | | addProductDevice(x01, "å¨åç³»ç»", "SYS-POWER", "æ¨è¿åç³»ç»", "SUB-PROP", "主æ¨è¿è£
ç½®", "DEV-PROP", propulsionDevice, 1); |
| | | addProductDevice(x01, "çµæ°ç³»ç»", "SYS-ELECTRIC", "çµååç³»ç»", "SUB-GRID", "主é
çµè£
ç½®", "DEV-DIST", distributionDevice, 2); |
| | | addProductDevice(x01, "ç²æ¿ç³»ç»", "SYS-DECK", "æ¶²ååç³»ç»", "SUB-HYD", "æ¶²åæ³µç»", "DEV-HYD", hydraulicDevice, 3); |
| | | addProductDevice(x02, "å¨åç³»ç»", "SYS-POWER", "ä¼ å¨åç³»ç»", "SUB-TRANS", "åé齿轮箱", "DEV-GEAR", gearDevice, 1); |
| | | addProductDevice(x02, "çµæ°ç³»ç»", "SYS-ELECTRIC", "æ§å¶åç³»ç»", "SUB-CTRL", "é䏿§å¶æ", "DEV-CONTROL", controlDevice, 2); |
| | | addProductDevice(x02, "ç²æ¿ç³»ç»", "SYS-DECK", "è¾
æºåç³»ç»", "SUB-AUX", "空æ°å缩æºç»", "DEV-AIR", compressorDevice, 3); |
| | | addProductDevice(x03, "å¨åç³»ç»", "SYS-POWER", "æ¨è¿åç³»ç»", "SUB-PROP", "主æ¨è¿è£
ç½®", "DEV-PROP", propulsionDevice, 1); |
| | | addProductDevice(x03, "çµæ°ç³»ç»", "SYS-ELECTRIC", "çµååç³»ç»", "SUB-GRID", "主é
çµè£
ç½®", "DEV-DIST", distributionDevice, 2); |
| | | addProductDevice(x03, "ç²æ¿ç³»ç»", "SYS-DECK", "æ¶²ååç³»ç»", "SUB-HYD", "æ¶²åæ³µç»", "DEV-HYD", hydraulicDevice, 3); |
| | | addProductDevice(x01, "çµæ°ç³»ç»", "SYS-ELECTRIC", "æ§å¶åç³»ç»", "SUB-CTRL", "é䏿§å¶æ", "DEV-CONTROL", controlDevice, 4); |
| | | addProductDevice(x02, "å¨åç³»ç»", "SYS-POWER", "æ¨è¿åç³»ç»", "SUB-PROP", "主æ¨è¿è£
ç½®æ¹è¿å", "DEV-PROP-X02", propulsionDevice, 4); |
| | | addProductDevice(x03, "çµæ°ç³»ç»", "SYS-ELECTRIC", "æ§å¶åç³»ç»", "SUB-CTRL", "é䏿§å¶æ", "DEV-CONTROL", controlDevice, 4); |
| | | addProductDevice(x03, "ç²æ¿ç³»ç»", "SYS-DECK", "è¾
æºåç³»ç»", "SUB-AUX", "空æ°å缩æºç»", "DEV-AIR", compressorDevice, 4); |
| | | } |
| | | } |
| | | |
| | |
| | | Fault fault = baseFault(code, xNo, equipment, team, nature, date, description); |
| | | fault.setTaskStatus(status); |
| | | if (!StatusConstants.Task.UNREPORTED.equals(status)) { |
| | | List<Long> ids = Arrays.asList(contractorIds.get(Math.abs(code.hashCode()) % 7), contractorIds.get((Math.abs(code.hashCode()) + 1) % 7)); |
| | | String names = contractorMapper.selectBatchIds(ids).stream().map(Contractor::getName).collect(java.util.stream.Collectors.joining("ã")); |
| | | int size = contractorIds.size(); |
| | | List<Long> ids = Arrays.asList( |
| | | contractorIds.get(Math.abs(code.hashCode()) % size), |
| | | contractorIds.get((Math.abs(code.hashCode()) + 1) % size)); |
| | | String names = ids.stream().map(contractorNames::get) |
| | | .filter(java.util.Objects::nonNull) |
| | | .collect(java.util.stream.Collectors.joining("ã")); |
| | | fault.setContractorIds(ids.stream().map(String::valueOf).collect(java.util.stream.Collectors.joining(","))); |
| | | fault.setContractorNames(names); |
| | | fault.setEngineeringNo("LX-" + xNo + "-" + date.toString().replace("-", "").substring(2) + "-001"); |
| | |
| | | Fault fault = baseFault(code, xNo, equipment, team, nature, date, description); |
| | | fault.setTaskStatus(StatusConstants.Task.JCC_DIRECTOR_APPROVED); |
| | | Long contractorId = contractorIds.get(contractorIndex % contractorIds.size()); |
| | | Contractor contractor = contractorMapper.selectById(contractorId); |
| | | fault.setContractorIds(String.valueOf(contractorId)); |
| | | fault.setContractorNames(contractor.getName()); |
| | | fault.setContractorNames(contractorNames.get(contractorId)); |
| | | fault.setEngineeringNo("LX-" + xNo + "-" + date.toString().replace("-", "").substring(2) + "-001"); |
| | | fault.setXcbReviewTime(LocalDateTime.now().minusDays(5)); |
| | | faultMapper.insert(fault); |
| | |
| | | } |
| | | } |
| | | |
| | | private void ensureDemoProductDevices() { |
| | | loadBaseIds(); |
| | | ensureProductDevice("X01", "çµæ°ç³»ç»", "SYS-ELECTRIC", "æ§å¶åç³»ç»", "SUB-CTRL", "é䏿§å¶æ", "DEV-CONTROL", "DEV-CONTROL", 4); |
| | | ensureProductDevice("X02", "å¨åç³»ç»", "SYS-POWER", "æ¨è¿åç³»ç»", "SUB-PROP", "主æ¨è¿è£
ç½®æ¹è¿å", "DEV-PROP-X02", "DEV-PROP", 4); |
| | | ensureProductDevice("X03", "çµæ°ç³»ç»", "SYS-ELECTRIC", "æ§å¶åç³»ç»", "SUB-CTRL", "é䏿§å¶æ", "DEV-CONTROL", "DEV-CONTROL", 4); |
| | | ensureProductDevice("X03", "ç²æ¿ç³»ç»", "SYS-DECK", "è¾
æºåç³»ç»", "SUB-AUX", "空æ°å缩æºç»", "DEV-AIR", "DEV-AIR", 4); |
| | | } |
| | | |
| | | private void ensureProductDevice(String xNo, String system1, String code1, String system2, String code2, |
| | | String device, String deviceCode, String baseCode, int sort) { |
| | | if (productDeviceIds.containsKey(deviceKey(xNo, device))) { |
| | | return; |
| | | } |
| | | ProductNode xNode = productNode(2, xNo); |
| | | Long baseId = baseIds.get(baseCode); |
| | | if (xNode == null || baseId == null) { |
| | | return; |
| | | } |
| | | addProductDevice(xNode, system1, code1, system2, code2, device, deviceCode, baseId, sort); |
| | | } |
| | | |
| | | private void loadBaseIds() { |
| | | if (!baseIds.isEmpty()) { |
| | | return; |
| | | } |
| | | for (BaseEquipment node : baseEquipmentMapper.selectList(null)) { |
| | | if (StringUtils.hasText(node.getCode())) { |
| | | baseIds.putIfAbsent(node.getCode(), node.getId()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private ProductNode productNode(int level, String name) { |
| | | List<ProductNode> nodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .eq(ProductNode::getNodeLevel, level) |
| | | .eq(ProductNode::getName, name) |
| | | .last("LIMIT 1")); |
| | | return nodes.isEmpty() ? null : nodes.get(0); |
| | | } |
| | | |
| | | private void backfillFaultXNoFromEquipment() { |
| | | List<ProductNode> nodes = productNodeMapper.selectList(null); |
| | |
| | | } |
| | | } |
| | | |
| | | private void normalizeSensitiveTerms() { |
| | | jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'XCB', 'rk_XCB') WHERE NAME LIKE '%XCB%' AND NAME NOT LIKE '%rk_XCB%'"); |
| | | jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'CMB', 'rk_CMB') WHERE NAME LIKE '%CMB%' AND NAME NOT LIKE '%rk_CMB%'"); |
| | | jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'JCC', 'rk_JCC') WHERE NAME LIKE '%JCC%' AND NAME NOT LIKE '%rk_JCC%'"); |
| | | jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'Té', 'rk_Té') WHERE NAME LIKE '%Té%' AND NAME NOT LIKE '%rk_Té%'"); |
| | | jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'Tå', 'rk_Tå') WHERE NAME LIKE '%Tå%' AND NAME NOT LIKE '%rk_Tå%'"); |
| | | jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'æTç»è®¡', 'rk_æTç»è®¡') WHERE NAME LIKE '%æTç»è®¡%' AND NAME NOT LIKE '%rk_æTç»è®¡%'"); |
| | | jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, 'æ¶H', 'rk_æ¶H') WHERE NAME LIKE '%æ¶H%' AND NAME NOT LIKE '%rk_æ¶H%'"); |
| | | } |
| | | |
| | | private void normalizeEquipmentTerms() { |
| | | jdbcTemplate.update("UPDATE LX.T_SYS_MENU SET NAME = REPLACE(NAME, '\u88c5\u5907', '\u8bbe\u5907') WHERE NAME LIKE '%\u88c5\u5907%'"); |
| | | } |
| | | |
| | | private void normalizeParentSystemSeparators() { |
| | | for (Fault fault : faultMapper.selectList(null)) { |
| | | String normalized = normalizeParentSystem(fault.getParentSystem()); |
| | |
| | | fault.setParentSystem(equipment.contains("æ¶²å") ? "ç²æ¿ç³»ç»" : equipment.contains("æ§å¶") ? "çµæ°ç³»ç»" : "å¨åç³»ç»"); |
| | | fault.setEngineeringMajor(team); |
| | | fault.setTeamMajor(team); |
| | | fault.setReportUnit(team + "Té"); |
| | | fault.setReportUnit(team + "rk_Té"); |
| | | fault.setReportDate(date); |
| | | fault.setOwner("Tå" + xNo); |
| | | fault.setOwner("rk_Tå" + xNo); |
| | | fault.setFaultImpact("便°".equals(team) ? "便°" : "è¿è¡"); |
| | | fault.setFaultNature(nature); |
| | | fault.setEngineeringNameDesc(description); |
| | |
| | | node.setCreateTime(LocalDateTime.now()); |
| | | node.setUpdateTime(LocalDateTime.now()); |
| | | baseEquipmentMapper.insert(node); |
| | | baseIds.put(code, node.getId()); |
| | | return node.getId(); |
| | | } |
| | | |
| | | private ProductNode product(String name, String code, Long parentId, int level, String type, Long baseEquipmentId, int sort) { |
| | | ProductNode node = new ProductNode(); |
| | | node.setParentId(parentId == null ? 0L : parentId); |
| | | node.setNodeLevel(level); |
| | | node.setNodeType(type); |
| | | node.setName(name); |
| | | node.setCode(code); |
| | | node.setBaseEquipmentId(baseEquipmentId); |
| | | node.setSortNo(sort); |
| | | node.setCreateTime(LocalDateTime.now()); |
| | | node.setUpdateTime(LocalDateTime.now()); |
| | | productNodeMapper.insert(node); |
| | | return node; |
| | | } |
| | | |
| | | private void addProductDevice(ProductNode xNode, String system1, String code1, String system2, String code2, |
| | | String device, String code, Long baseId, int sort) { |
| | | ProductNode node1 = product(system1, code1 + "-" + xNode.getCode(), xNode.getId(), 3, "SYSTEM1", null, sort); |
| | | ProductNode node2 = product(system2, code2 + "-" + xNode.getCode(), node1.getId(), 4, "SYSTEM2", null, 1); |
| | | product(device, code + "-" + xNode.getCode(), node2.getId(), 5, "DEVICE", baseId, 1); |
| | | } |
| | | } |
| | |
| | | @Order(1) |
| | | @RequiredArgsConstructor |
| | | public class DatabaseBootstrap implements CommandLineRunner { |
| | | private static final String[] AUDIT_TABLES = { |
| | | "T_FAULT", "T_ENGINEERING_MAJOR", "T_TEAM_MAJOR", "T_CONTRACTOR", |
| | | "T_BASE_EQUIPMENT", "T_PRODUCT_TREE", "T_WORK_ORDER", "T_COMPLETION_INFO", |
| | | "T_ACCEPTANCE", "T_SYS_MENU", "T_SYS_ROLE", "T_SYS_ROLE_MENU", |
| | | "T_SYS_TENANT_MENU", "T_SYS_ROLE_USER", "T_SYS_LOGIN_LOG", "T_SYS_OPERATION_LOG" |
| | | }; |
| | | private final JdbcTemplate jdbcTemplate; |
| | | |
| | | @Override |
| | |
| | | jdbcTemplate.execute(entry.getValue()); |
| | | } |
| | | } |
| | | ensureAuditColumns(); |
| | | renameColumnIfNeeded("T_FAULT", "FAULT_DESCRIPTION", "ENGINEERING_NAME_DESC", "ALTER TABLE LX.T_FAULT RENAME COLUMN FAULT_DESCRIPTION TO ENGINEERING_NAME_DESC"); |
| | | ensureColumn("T_FAULT", "EQUIPMENT_IDS", "ALTER TABLE LX.T_FAULT ADD EQUIPMENT_IDS VARCHAR(2000)"); |
| | | ensureColumn("T_COMPLETION_INFO", "CONSUMABLE_COUNT", "ALTER TABLE LX.T_COMPLETION_INFO ADD CONSUMABLE_COUNT INT"); |
| | |
| | | ensureIndex("UX_T_WORK_ORDER_NO", "CREATE UNIQUE INDEX LX.UX_T_WORK_ORDER_NO ON LX.T_WORK_ORDER(WORK_ORDER_NO)"); |
| | | ensureIndex("IX_T_WORK_ORDER_FAULT_ID", "CREATE INDEX LX.IX_T_WORK_ORDER_FAULT_ID ON LX.T_WORK_ORDER(FAULT_ID)"); |
| | | ensureIndex("IX_T_COMPLETION_WORK_ORDER", "CREATE INDEX LX.IX_T_COMPLETION_WORK_ORDER ON LX.T_COMPLETION_INFO(WORK_ORDER_ID)"); |
| | | ensureIndex("IX_T_SYS_LOGIN_LOG_TIME", "CREATE INDEX LX.IX_T_SYS_LOGIN_LOG_TIME ON LX.T_SYS_LOGIN_LOG(LOGIN_TIME)"); |
| | | ensureIndex("IX_T_SYS_OPERATION_LOG_TIME", "CREATE INDEX LX.IX_T_SYS_OPERATION_LOG_TIME ON LX.T_SYS_OPERATION_LOG(CREATE_TIME)"); |
| | | ensureIndex("IX_T_SYS_ROLE_USER_USER", "CREATE INDEX LX.IX_T_SYS_ROLE_USER_USER ON LX.T_SYS_ROLE_USER(USER_ID)"); |
| | | } |
| | | |
| | | private void ensureAuditColumns() { |
| | | for (String tableName : AUDIT_TABLES) { |
| | | if (!tableExists(tableName)) { |
| | | continue; |
| | | } |
| | | ensureColumn(tableName, "CREATE_BY", "ALTER TABLE LX." + tableName + " ADD CREATE_BY VARCHAR(120)"); |
| | | ensureColumn(tableName, "UPDATE_BY", "ALTER TABLE LX." + tableName + " ADD UPDATE_BY VARCHAR(120)"); |
| | | ensureColumn(tableName, "DEL_FLAG", "ALTER TABLE LX." + tableName + " ADD DEL_FLAG INT DEFAULT 0"); |
| | | jdbcTemplate.update("UPDATE LX." + tableName + " SET DEL_FLAG = 0 WHERE DEL_FLAG IS NULL"); |
| | | } |
| | | } |
| | | private void ensureSchema() { |
| | | Integer count = jdbcTemplate.queryForObject( |
| | | "SELECT COUNT(*) FROM ALL_USERS WHERE USERNAME = ?", Integer.class, "LX"); |
| | |
| | | ddl.put("T_SYS_ROLE_MENU", "CREATE TABLE LX.T_SYS_ROLE_MENU (ID BIGINT NOT NULL PRIMARY KEY, ROLE_ID BIGINT, MENU_ID BIGINT, CREATE_TIME TIMESTAMP)"); |
| | | ddl.put("T_SYS_TENANT_MENU", "CREATE TABLE LX.T_SYS_TENANT_MENU (ID BIGINT NOT NULL PRIMARY KEY, TENANT_ID BIGINT, MENU_ID BIGINT, CREATE_TIME TIMESTAMP)"); |
| | | ddl.put("T_SYS_ROLE_USER", "CREATE TABLE LX.T_SYS_ROLE_USER (ID BIGINT NOT NULL PRIMARY KEY, ROLE_ID BIGINT, USER_ID BIGINT, CREATE_TIME TIMESTAMP)"); |
| | | ddl.put("T_SYS_LOGIN_LOG", "CREATE TABLE LX.T_SYS_LOGIN_LOG (ID BIGINT NOT NULL PRIMARY KEY, USER_ID BIGINT, USER_NAME VARCHAR(120), NICK_NAME VARCHAR(120), DEPT_NAME VARCHAR(120), LOGIN_IP VARCHAR(200), STATUS VARCHAR(20), MESSAGE VARCHAR(500), LOGIN_TIME TIMESTAMP)"); |
| | | ddl.put("T_SYS_OPERATION_LOG", "CREATE TABLE LX.T_SYS_OPERATION_LOG (ID BIGINT NOT NULL PRIMARY KEY, USER_ID BIGINT, USER_NAME VARCHAR(120), NICK_NAME VARCHAR(120), MODULE VARCHAR(100), OPERATION_TYPE VARCHAR(40), TARGET_TYPE VARCHAR(100), TARGET_ID VARCHAR(80), DESCRIPTION VARCHAR(500), REQUEST_METHOD VARCHAR(10), REQUEST_URI VARCHAR(300), RESULT VARCHAR(20), CREATE_TIME TIMESTAMP)"); |
| | | return ddl; |
| | | } |
| | | } |
| | |
| | | |
| | | @GetMapping |
| | | public Result<List<WorkOrder>> list(@RequestParam(required = false) String status, |
| | | @RequestParam(required = false) String keyword) { |
| | | return Result.ok(businessService.listWorkOrders(status, keyword)); |
| | | @RequestParam(required = false) String keyword, |
| | | @RequestParam(required = false, defaultValue = "false") boolean completionReportable) { |
| | | return Result.ok(businessService.listWorkOrders(status, keyword, completionReportable)); |
| | | } |
| | | |
| | | @GetMapping("/{id}") |
| | |
| | | private List<Long> roleMenuIds = new ArrayList<>(); |
| | | private List<Long> tenantMenuIds = new ArrayList<>(); |
| | | } |
| | | @Data |
| | | public static class UserRoleRequest { |
| | | private List<Long> roleIds = new ArrayList<>(); |
| | | } |
| | | |
| | | @Data |
| | | public static class BatchUserRoleRequest { |
| | | private List<Long> userIds = new ArrayList<>(); |
| | | private List<Long> roleIds = new ArrayList<>(); |
| | | private String mode = "append"; |
| | | } |
| | | |
| | | @Data |
| | | public static class RoleUserRequest { |
| | | private List<Long> userIds = new ArrayList<>(); |
| | | } |
| | | |
| | | @Data |
| | | public static class ButtonSyncRequest { |
| | | private Long menuId; |
| | | private List<String> codes = new ArrayList<>(); |
| | | } |
| | | } |
| | |
| | | package com.emergencyrepair.business.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_ACCEPTANCE") |
| | | public class Acceptance { |
| | | public class Acceptance extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | package com.emergencyrepair.business.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_BASE_EQUIPMENT") |
| | | public class BaseEquipment { |
| | | public class BaseEquipment extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | package com.emergencyrepair.business.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_COMPLETION_INFO") |
| | | public class CompletionInfo { |
| | | public class CompletionInfo extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | package com.emergencyrepair.business.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_CONTRACTOR") |
| | | public class Contractor { |
| | | public class Contractor extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | package com.emergencyrepair.business.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_ENGINEERING_MAJOR") |
| | | public class EngineeringMajor { |
| | | public class EngineeringMajor extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | package com.emergencyrepair.business.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_FAULT") |
| | | public class Fault { |
| | | public class Fault extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.Locale; |
| | | |
| | | @Data |
| | | @TableName("LX.T_PRODUCT_TREE") |
| | | @TableName("LX.ZT_BOAT_TREE") |
| | | public class ProductNode { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | /** |
| | | * Business node id. The external tree uses INFO_ID for the node and |
| | | * CONNECT_ID for its parent, while ID is the physical relation row id. |
| | | */ |
| | | @TableId(value = "INFO_ID", type = IdType.INPUT) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | | |
| | | @TableField("ID") |
| | | private String recordId; |
| | | |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | @TableField("PARENT_ID") private Long parentId; |
| | | @TableField("NODE_LEVEL") private Integer nodeLevel; |
| | | @TableField("NODE_TYPE") private String nodeType; |
| | | @TableField("NAME") private String name; |
| | | @TableField("CODE") private String code; |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | @TableField("BASE_EQUIPMENT_ID") private Long baseEquipmentId; |
| | | @TableField("SORT_NO") private Integer sortNo; |
| | | @TableField("REMARK") private String remark; |
| | | @TableField("CREATE_TIME") private LocalDateTime createTime; |
| | | @TableField("UPDATE_TIME") private LocalDateTime updateTime; |
| | | @TableField("CONNECT_ID") |
| | | private Long parentId; |
| | | |
| | | @TableField("CONNECT_GROUP") |
| | | private String connectGroup; |
| | | |
| | | @TableField("LABEL") |
| | | private String name; |
| | | |
| | | @TableField("LAYER") |
| | | private String layer; |
| | | |
| | | @TableField("PROJECT") |
| | | private String project; |
| | | |
| | | @TableField("SORT") |
| | | private Integer sortNo; |
| | | |
| | | @TableField("ISDELETE") |
| | | private String isDelete; |
| | | |
| | | @TableField("CREATE_BY") |
| | | private String createBy; |
| | | |
| | | @TableField("CREATE_TIME") |
| | | private LocalDateTime createTime; |
| | | |
| | | @TableField(exist = false) |
| | | private Integer nodeLevel; |
| | | |
| | | @TableField(exist = false) |
| | | private String nodeType; |
| | | |
| | | @TableField(exist = false) |
| | | private String code; |
| | | |
| | | @TableField(exist = false) |
| | | private Long baseEquipmentId; |
| | | |
| | | @TableField(exist = false) |
| | | private String remark; |
| | | |
| | | @TableField(exist = false) |
| | | private LocalDateTime updateTime; |
| | | |
| | | public Integer getNodeLevel() { |
| | | if (nodeLevel != null) { |
| | | return nodeLevel; |
| | | } |
| | | return levelForLayer(layer); |
| | | } |
| | | |
| | | public void setNodeLevel(Integer nodeLevel) { |
| | | this.nodeLevel = nodeLevel; |
| | | if (layer == null && nodeLevel != null) { |
| | | layer = layerForLevel(nodeLevel); |
| | | } |
| | | } |
| | | |
| | | public String getNodeType() { |
| | | if (nodeType != null) { |
| | | return nodeType; |
| | | } |
| | | return typeForLayer(layer); |
| | | } |
| | | |
| | | public void setNodeType(String nodeType) { |
| | | this.nodeType = nodeType; |
| | | if (layer == null && nodeType != null) { |
| | | layer = layerForType(nodeType); |
| | | } |
| | | } |
| | | |
| | | public static Integer levelForLayer(String value) { |
| | | String layer = normalize(value); |
| | | if ("model".equals(layer)) { |
| | | return 1; |
| | | } |
| | | if ("side".equals(layer)) { |
| | | return 2; |
| | | } |
| | | if ("system1".equals(layer)) { |
| | | return 3; |
| | | } |
| | | if ("system2".equals(layer)) { |
| | | return 4; |
| | | } |
| | | if ("equipment".equals(layer)) { |
| | | return 5; |
| | | } |
| | | return layer.isEmpty() ? null : 6; |
| | | } |
| | | |
| | | public static String typeForLayer(String value) { |
| | | Integer level = levelForLayer(value); |
| | | if (level == null) { |
| | | return null; |
| | | } |
| | | if (level == 1) { |
| | | return "MODEL"; |
| | | } |
| | | if (level == 2) { |
| | | return "X_NO"; |
| | | } |
| | | if (level == 3) { |
| | | return "SYSTEM1"; |
| | | } |
| | | if (level == 4) { |
| | | return "SYSTEM2"; |
| | | } |
| | | if (level == 5) { |
| | | return "DEVICE"; |
| | | } |
| | | return normalize(value).toUpperCase(Locale.ROOT); |
| | | } |
| | | |
| | | public static String layerForLevel(Integer level) { |
| | | if (level == null) { |
| | | return null; |
| | | } |
| | | if (level == 1) { |
| | | return "model"; |
| | | } |
| | | if (level == 2) { |
| | | return "side"; |
| | | } |
| | | if (level == 3) { |
| | | return "system1"; |
| | | } |
| | | if (level == 4) { |
| | | return "system2"; |
| | | } |
| | | if (level == 5) { |
| | | return "equipment"; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | public static String layerForType(String type) { |
| | | String value = normalize(type); |
| | | if ("MODEL".equals(value)) { |
| | | return "model"; |
| | | } |
| | | if ("X_NO".equals(value) || "XNUMBER".equals(value)) { |
| | | return "side"; |
| | | } |
| | | if ("SYSTEM1".equals(value)) { |
| | | return "system1"; |
| | | } |
| | | if ("SYSTEM2".equals(value)) { |
| | | return "system2"; |
| | | } |
| | | if ("DEVICE".equals(value) || "EQUIPMENT".equals(value)) { |
| | | return "equipment"; |
| | | } |
| | | return value.isEmpty() ? null : value.toLowerCase(Locale.ROOT); |
| | | } |
| | | |
| | | private static String normalize(String value) { |
| | | return value == null ? "" : value.trim().toLowerCase(Locale.ROOT); |
| | | } |
| | | } |
| | |
| | | package com.emergencyrepair.business.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_TEAM_MAJOR") |
| | | public class TeamMajor { |
| | | public class TeamMajor extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | package com.emergencyrepair.business.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_WORK_ORDER") |
| | | public class WorkOrder { |
| | | public class WorkOrder extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | import com.emergencyrepair.business.dto.Requests; |
| | | import com.emergencyrepair.business.entity.Acceptance; |
| | | import com.emergencyrepair.business.entity.CompletionInfo; |
| | | import com.emergencyrepair.business.entity.Contractor; |
| | | import com.emergencyrepair.business.entity.Fault; |
| | | import com.emergencyrepair.business.entity.TeamMajor; |
| | | import com.emergencyrepair.business.entity.WorkOrder; |
| | | import com.emergencyrepair.business.mapper.AcceptanceMapper; |
| | | import com.emergencyrepair.business.mapper.CompletionInfoMapper; |
| | | import com.emergencyrepair.business.mapper.ContractorMapper; |
| | | import com.emergencyrepair.business.mapper.FaultMapper; |
| | | import com.emergencyrepair.business.mapper.TeamMajorMapper; |
| | | import com.emergencyrepair.business.mapper.WorkOrderMapper; |
| | | import com.emergencyrepair.common.BusinessException; |
| | | import com.emergencyrepair.common.StatusConstants; |
| | | import com.emergencyrepair.common.audit.CurrentUserContext; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.jdbc.core.JdbcTemplate; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Collection; |
| | | import java.util.Objects; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.function.Function; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class BusinessService { |
| | | private static final DateTimeFormatter FAULT_DATE_FORMAT = DateTimeFormatter.ofPattern("yyMMdd"); |
| | | private static final String FAULT_CODE_PREFIX = "\u6f5c1\u57fa-"; |
| | | private static final Object FAULT_CODE_LOCK = new Object(); |
| | | @Value("${fault-code.prefix:\u6f5c1\u57fa-}") |
| | | private String faultCodePrefix; |
| | | private final FaultMapper faultMapper; |
| | | private final JdbcTemplate jdbcTemplate; |
| | | private final TeamMajorMapper teamMajorMapper; |
| | | private final WorkOrderMapper workOrderMapper; |
| | | private final ContractorMapper contractorMapper; |
| | | private final CompletionInfoMapper completionInfoMapper; |
| | | private final AcceptanceMapper acceptanceMapper; |
| | | private final ObjectMapper objectMapper; |
| | |
| | | private String generateFaultCode(Fault fault) { |
| | | String xNo = normalizeXNo(fault.getXNo()); |
| | | if (xNo == null) { |
| | | throw new BusinessException("è¯·éæ©Xå·ï¼æ ¼å¼ä¸º3使°å"); |
| | | throw new BusinessException("è¯·éæ©rk_Xå·ï¼æ ¼å¼ä¸º3使°å"); |
| | | } |
| | | if (!StringUtils.hasText(fault.getTeamMajor())) { |
| | | throw new BusinessException("è¯·éæ©Téä¸ä¸"); |
| | | throw new BusinessException("è¯·éæ©rk_Téä¸ä¸"); |
| | | } |
| | | TeamMajor teamMajor = teamMajorMapper.selectOne(Wrappers.<TeamMajor>lambdaQuery() |
| | | .eq(TeamMajor::getName, fault.getTeamMajor().trim()) |
| | | .last("LIMIT 1")); |
| | | if (teamMajor == null) { |
| | | throw new BusinessException("Téä¸ä¸æªé
ç½®ï¼è¯·å
å¨ç³»ç»ç®¡ç > Téä¸ä¸é
ç½®ä¸ç»´æ¤ï¼" + fault.getTeamMajor()); |
| | | throw new BusinessException("rk_Téä¸ä¸æªé
ç½®ï¼è¯·å
å¨ç³»ç»ç®¡ç > rk_Téä¸ä¸é
ç½®ä¸ç»´æ¤ï¼" + fault.getTeamMajor()); |
| | | } |
| | | String category = teamMajor.getCategory() == null ? "" : teamMajor.getCategory().trim().toUpperCase(); |
| | | if (!"A".equals(category) && !"B".equals(category)) { |
| | | throw new BusinessException("Téä¸ä¸åç±»åªè½ä¸ºAæBï¼" + fault.getTeamMajor()); |
| | | throw new BusinessException("rk_Téä¸ä¸åç±»åªè½ä¸ºAæBï¼" + fault.getTeamMajor()); |
| | | } |
| | | String prefix = FAULT_CODE_PREFIX + xNo + "/" + category + "-" + fault.getReportDate().format(FAULT_DATE_FORMAT) + "-"; |
| | | String prefix = faultCodePrefix + xNo + "/" + category + "-" + fault.getReportDate().format(FAULT_DATE_FORMAT) + "-"; |
| | | int next = 1; |
| | | for (Fault item : faultMapper.selectList(Wrappers.<Fault>lambdaQuery() |
| | | .likeRight(Fault::getFaultCode, prefix))) { |
| | | next = Math.max(next, parseFaultSequence(item.getFaultCode(), prefix) + 1); |
| | | // Include logically deleted rows so a generated code never collides with |
| | | // the unique index on FAULT_CODE. |
| | | List<String> existingCodes = jdbcTemplate.query( |
| | | "SELECT FAULT_CODE FROM LX.T_FAULT WHERE FAULT_CODE LIKE ?", |
| | | new Object[]{prefix + "%"}, (rs, rowNum) -> rs.getString(1)); |
| | | for (String existingCode : existingCodes) { |
| | | next = Math.max(next, parseFaultSequence(existingCode, prefix) + 1); |
| | | } |
| | | return prefix + String.format("%03d", next); |
| | | } |
| | |
| | | throw new BusinessException("请填åé项çç±"); |
| | | } |
| | | String cancelledBy = StringUtils.hasText(request.getCancelledBy()) |
| | | ? request.getCancelledBy().trim() : "XCBå©ç"; |
| | | ? request.getCancelledBy().trim() : "rk_XCBå©ç"; |
| | | for (Long id : request.getIds()) { |
| | | Fault fault = requireFault(id); |
| | | if (!StatusConstants.Task.REPORTED_XCB.equals(fault.getTaskStatus()) |
| | |
| | | if (request.getContractorIds() == null || request.getContractorIds().isEmpty()) { |
| | | throw new BusinessException("è³å°åé
ä¸ä¸ªæ¿ä¿®åä½"); |
| | | } |
| | | List<Contractor> contractors = contractorMapper.selectBatchIds(request.getContractorIds()); |
| | | Map<Long, Contractor> contractorMap = contractors.stream() |
| | | .collect(Collectors.toMap(Contractor::getId, Function.identity())); |
| | | Map<Long, String> contractorMap = contractorNamesByIds(request.getContractorIds()); |
| | | if (contractorMap.isEmpty()) { |
| | | throw new BusinessException("æ¿ä¿®åä½ä¸å卿æªå¨é¨é¨è¡¨ä¸é
ç½®"); |
| | | } |
| | | String contractorIds = request.getContractorIds().stream() |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(",")); |
| | | String contractorNames = request.getContractorIds().stream() |
| | | .map(contractorMap::get) |
| | | .filter(item -> item != null) |
| | | .map(Contractor::getName) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.joining("ã")); |
| | | |
| | | int index = 0; |
| | |
| | | Fault fault = requireFault(id); |
| | | if (!StatusConstants.Task.REPORTED_XCB.equals(fault.getTaskStatus()) |
| | | && !StatusConstants.Task.RETURNED.equals(fault.getTaskStatus())) { |
| | | throw new BusinessException("æ
é " + fault.getFaultCode() + " å½åç¶æä¸å
许XCBå©çå®¡æ ¸"); |
| | | throw new BusinessException("æ
é " + fault.getFaultCode() + " å½åç¶æä¸å
许rk_XCBå©çå®¡æ ¸"); |
| | | } |
| | | index++; |
| | | fault.setContractorIds(contractorIds); |
| | |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | switch (request.getStage()) { |
| | | case "CMB": |
| | | requireStatus(fault, StatusConstants.Task.XCB_REVIEWED, "CMBé¢å¯¼å®¡æ¹"); |
| | | requireStatus(fault, StatusConstants.Task.XCB_REVIEWED, "rk_CMBé¢å¯¼å®¡æ¹"); |
| | | 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, "JCCå©ç审æ¹"); |
| | | requireStatus(fault, StatusConstants.Task.CMB_APPROVED, "rk_JCCå©ç审æ¹"); |
| | | 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, "JCCå¤é¿å®¡æ¹"); |
| | | requireStatus(fault, StatusConstants.Task.JCC_ASSISTANT_APPROVED, "rk_JCCå¤é¿å®¡æ¹"); |
| | | fault.setJccDirectorOpinion(request.getOpinion()); |
| | | fault.setJccDirectorResult(pass ? "éè¿" : "ä¸éè¿"); |
| | | fault.setJccDirectorTime(now); |
| | |
| | | if (exists > 0) { |
| | | throw new BusinessException("æ
é " + fault.getFaultCode() + " å·²ç»æ´¾å"); |
| | | } |
| | | List<Contractor> contractors = contractorMapper.selectBatchIds(contractorIds); |
| | | Map<Long, Contractor> contractorMap = contractors.stream() |
| | | .collect(Collectors.toMap(Contractor::getId, Function.identity())); |
| | | Map<Long, String> contractorMap = contractorNamesByIds(contractorIds); |
| | | int sequence = 1; |
| | | for (Long contractorId : contractorIds) { |
| | | Contractor contractor = contractorMap.get(contractorId); |
| | | if (contractor == null) { |
| | | String contractorName = contractorMap.get(contractorId); |
| | | if (contractorName == null) { |
| | | continue; |
| | | } |
| | | WorkOrder workOrder = workOrderFromFault(fault); |
| | | workOrder.setWorkOrderNo(fault.getFaultCode() + "/" + String.format("%02d", sequence++)); |
| | | workOrder.setContractorId(contractor.getId()); |
| | | workOrder.setContractorName(contractor.getName()); |
| | | workOrder.setContractorId(contractorId); |
| | | workOrder.setContractorName(contractorName); |
| | | workOrder.setCompletionStatus(StatusConstants.Completion.UNFINISHED); |
| | | workOrder.setDispatchTime(LocalDateTime.now()); |
| | | workOrder.setCreateTime(LocalDateTime.now()); |
| | |
| | | return created; |
| | | } |
| | | |
| | | public List<WorkOrder> listWorkOrders(String status, String keyword) { |
| | | public List<WorkOrder> listWorkOrders(String status, String keyword, boolean completionReportable) { |
| | | LambdaQueryWrapper<WorkOrder> query = Wrappers.lambdaQuery(); |
| | | if (StringUtils.hasText(status)) { |
| | | if (completionReportable) { |
| | | query.eq(WorkOrder::getCompletionStatus, StatusConstants.Completion.COMPLETED) |
| | | .notInSql(WorkOrder::getId, "SELECT WORK_ORDER_ID FROM LX.T_COMPLETION_INFO WHERE DEL_FLAG = 0 OR DEL_FLAG IS NULL"); |
| | | List<String> unitNames = currentUserContractorNames(); |
| | | if (unitNames != null) { |
| | | if (unitNames.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | query.in(WorkOrder::getContractorName, unitNames); |
| | | } |
| | | } else if (StringUtils.hasText(status)) { |
| | | query.in(WorkOrder::getCompletionStatus, Arrays.asList(status.split(","))); |
| | | } |
| | | if (StringUtils.hasText(keyword)) { |
| | |
| | | return workOrderMapper.selectList(query); |
| | | } |
| | | |
| | | /** |
| | | * Returns null when the current user sees every unit (system administrator). |
| | | * Otherwise returns the contractor names matching the logged-in user's department. |
| | | */ |
| | | private List<String> currentUserContractorNames() { |
| | | String username = CurrentUserContext.get(); |
| | | if (!StringUtils.hasText(username) || "admin".equalsIgnoreCase(username)) { |
| | | return null; |
| | | } |
| | | List<Map<String, Object>> rows = jdbcTemplate.query( |
| | | "SELECT d.DEPT_NAME, d.DEPT_TYPE FROM CSICZB_ZB.SYS_USER u " |
| | | + "LEFT JOIN CSICZB_ZB.SYS_DEPT d ON d.DEPT_ID = u.DEPT_ID " |
| | | + "WHERE u.USER_NAME = ? AND (u.DEL_FLAG = '0' OR u.DEL_FLAG IS NULL)", |
| | | new Object[]{username}, (rs, rowNum) -> { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | row.put("deptName", rs.getString("DEPT_NAME")); |
| | | row.put("deptType", rs.getString("DEPT_TYPE")); |
| | | return row; |
| | | }); |
| | | if (rows.isEmpty() || !StringUtils.hasText((String) rows.get(0).get("deptName"))) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<String, Object> dept = rows.get(0); |
| | | if (!"cj".equalsIgnoreCase(String.valueOf(dept.get("deptType")))) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return Collections.singletonList(((String) dept.get("deptName")).trim()); |
| | | } |
| | | |
| | | private Map<Long, String> contractorNamesByIds(Collection<Long> ids) { |
| | | Map<Long, String> result = new LinkedHashMap<>(); |
| | | if (ids == null || ids.isEmpty()) { |
| | | return result; |
| | | } |
| | | String placeholders = ids.stream().map(item -> "?").collect(Collectors.joining(",")); |
| | | List<Object> args = new ArrayList<>(ids); |
| | | return jdbcTemplate.query( |
| | | "SELECT DEPT_ID, DEPT_NAME FROM CSICZB_ZB.SYS_DEPT " |
| | | + "WHERE DEPT_TYPE = 'cj' AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL) " |
| | | + "AND DEPT_ID IN (" + placeholders + ")", |
| | | args.toArray(), rs -> { |
| | | while (rs.next()) { |
| | | result.put(rs.getLong("DEPT_ID"), rs.getString("DEPT_NAME")); |
| | | } |
| | | return result; |
| | | }); |
| | | } |
| | | |
| | | public WorkOrder getWorkOrder(Long id) { |
| | | return workOrderMapper.selectById(id); |
| | | } |
| | |
| | | @Transactional |
| | | public void updateCompletionStatus(Requests.StatusRequest request) { |
| | | WorkOrder workOrder = requireWorkOrder(request.getId()); |
| | | if (StatusConstants.Completion.COMPLETED.equals(workOrder.getCompletionStatus())) { |
| | | if (StatusConstants.Completion.COMPLETED.equals(workOrder.getCompletionStatus()) |
| | | && !StatusConstants.Completion.COMPLETED.equals(request.getStatus())) { |
| | | throw new BusinessException("å·²å®å·¥æ´¾å·¥åä¸è½éåæ®éå®å·¥ç¶æ"); |
| | | } |
| | | if (!Arrays.asList( |
| | | StatusConstants.Completion.UNFINISHED, |
| | | StatusConstants.Completion.WAIT_PARTS, |
| | | StatusConstants.Completion.WAIT_TEST).contains(request.getStatus())) { |
| | | StatusConstants.Completion.WAIT_TEST, |
| | | StatusConstants.Completion.COMPLETED).contains(request.getStatus())) { |
| | | throw new BusinessException("å®å·¥ç¶æä¸åæ³"); |
| | | } |
| | | workOrder.setCompletionStatus(request.getStatus()); |
| | |
| | | @Transactional |
| | | public CompletionInfo submitCompletion(Requests.CompletionRequest request) { |
| | | WorkOrder workOrder = requireWorkOrder(request.getWorkOrderId()); |
| | | if (StatusConstants.Completion.COMPLETED.equals(workOrder.getCompletionStatus())) { |
| | | CompletionInfo info = completionInfoMapper.selectOne(Wrappers.<CompletionInfo>lambdaQuery() |
| | | .eq(CompletionInfo::getWorkOrderId, workOrder.getId()).last("LIMIT 1")); |
| | | if (info != null) { |
| | | throw new BusinessException("该派工åå·²å®å·¥ä¸æ¥ï¼ä¸è½éå¤æäº¤"); |
| | | } |
| | | if (!StatusConstants.Completion.COMPLETED.equals(workOrder.getCompletionStatus())) { |
| | | throw new BusinessException("该派工åå°æªå¨å®å·¥æ
åµå¤çä¸ç¡®è®¤å®å·¥ï¼ä¸è½å¡«æ¥å®å·¥ä¿¡æ¯"); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | workOrder.setCompletionStatus(StatusConstants.Completion.COMPLETED); |
| | | workOrder.setUpdateTime(now); |
| | | workOrderMapper.updateById(workOrder); |
| | | |
| | | CompletionInfo info = completionInfoMapper.selectOne(Wrappers.<CompletionInfo>lambdaQuery() |
| | | .eq(CompletionInfo::getWorkOrderId, workOrder.getId()).last("LIMIT 1")); |
| | | if (info == null) { |
| | | info = new CompletionInfo(); |
| | | info.setWorkOrderId(workOrder.getId()); |
| | | info.setCreateTime(now); |
| | | } |
| | | info.setWorkOrderNo(workOrder.getWorkOrderNo()); |
| | | info.setXNo(workOrder.getXNo()); |
| | | info.setContractorName(workOrder.getContractorName()); |
| | |
| | | ids.add(fault.getEquipmentId()); |
| | | } |
| | | if (ids.isEmpty()) { |
| | | throw new BusinessException("è¯·éæ©è£
å¤åç§°"); |
| | | throw new BusinessException("è¯·éæ©è®¾å¤åç§°"); |
| | | } |
| | | List<Long> normalized = new ArrayList<>(ids); |
| | | fault.setEquipmentIds(normalized.stream().map(String::valueOf).collect(Collectors.joining(","))); |
| | |
| | | report.setChart(Arrays.asList( |
| | | metric("å·¥ç¨æ»æ°", report.getEngineeringTotal()), |
| | | metric("é大æ
é", report.getMajorFault()), |
| | | metric("æ¶Hç»è®¡", report.getHRelated()), |
| | | metric("rk_æ¶Hç»è®¡", report.getHRelated()), |
| | | metric("çæµé¡¹ç®", report.getMonitorItem()), |
| | | metric("æªå®å·¥", report.getUnfinished()), |
| | | metric("å·²å®å·¥", report.getCompleted()), |
| New file |
| | |
| | | package com.emergencyrepair.common.audit; |
| | | |
| | | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
| | | import org.apache.ibatis.reflection.MetaObject; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * Populates create/update actor and logical-delete state automatically. |
| | | */ |
| | | @Component |
| | | public class AuditMetaObjectHandler implements MetaObjectHandler { |
| | | @Override |
| | | public void insertFill(MetaObject metaObject) { |
| | | String username = CurrentUserContext.get(); |
| | | strictInsertFill(metaObject, "createBy", String.class, username); |
| | | strictInsertFill(metaObject, "updateBy", String.class, username); |
| | | strictInsertFill(metaObject, "delFlag", Integer.class, 0); |
| | | } |
| | | |
| | | @Override |
| | | public void updateFill(MetaObject metaObject) { |
| | | strictUpdateFill(metaObject, "updateBy", String.class, CurrentUserContext.get()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.emergencyrepair.common.audit; |
| | | |
| | | /** |
| | | * Carries the request user through the current request thread for MyBatis fills. |
| | | */ |
| | | public final class CurrentUserContext { |
| | | private static final ThreadLocal<String> USERNAME = new ThreadLocal<>(); |
| | | |
| | | private CurrentUserContext() { |
| | | } |
| | | |
| | | public static void set(String username) { |
| | | USERNAME.set(username); |
| | | } |
| | | |
| | | public static String get() { |
| | | String username = USERNAME.get(); |
| | | return username == null || username.trim().isEmpty() ? "admin" : username.trim(); |
| | | } |
| | | |
| | | public static void clear() { |
| | | USERNAME.remove(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.emergencyrepair.common.audit; |
| | | |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.servlet.HandlerInterceptor; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | |
| | | @Component |
| | | public class CurrentUserInterceptor implements HandlerInterceptor { |
| | | @Override |
| | | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { |
| | | CurrentUserContext.set(resolveUsername(request)); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, |
| | | Object handler, Exception exception) { |
| | | CurrentUserContext.clear(); |
| | | } |
| | | |
| | | private String resolveUsername(HttpServletRequest request) { |
| | | String username = request.getHeader("X-Username"); |
| | | if (StringUtils.hasText(username)) { |
| | | return username.trim(); |
| | | } |
| | | String authorization = request.getHeader("Authorization"); |
| | | if (!StringUtils.hasText(authorization)) { |
| | | return "admin"; |
| | | } |
| | | String token = authorization.trim(); |
| | | if (token.startsWith("Bearer ")) { |
| | | token = token.substring(7).trim(); |
| | | } |
| | | if (token.startsWith("demo-") && token.endsWith("-token")) { |
| | | token = token.substring(5, token.length() - 6); |
| | | } |
| | | return StringUtils.hasText(token) ? token : "admin"; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.emergencyrepair.common.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * Shared audit and logical-delete fields for application-owned tables. |
| | | */ |
| | | @Data |
| | | public abstract class BaseAuditEntity { |
| | | @TableField(value = "CREATE_BY", fill = FieldFill.INSERT) |
| | | private String createBy; |
| | | |
| | | @TableField(value = "UPDATE_BY", fill = FieldFill.INSERT_UPDATE) |
| | | private String updateBy; |
| | | |
| | | @TableLogic(value = "0", delval = "1") |
| | | @TableField("DEL_FLAG") |
| | | private Integer delFlag; |
| | | } |
| | |
| | | package com.emergencyrepair.config; |
| | | |
| | | import com.emergencyrepair.common.audit.CurrentUserInterceptor; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import com.emergencyrepair.system.log.OperationLogInterceptor; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.servlet.config.annotation.CorsRegistry; |
| | | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
| | | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
| | | |
| | | @RequiredArgsConstructor |
| | | @Configuration |
| | | public class WebConfig implements WebMvcConfigurer { |
| | | private final CurrentUserInterceptor currentUserInterceptor; |
| | | private final OperationLogInterceptor operationLogInterceptor; |
| | | |
| | | @Override |
| | | public void addInterceptors(InterceptorRegistry registry) { |
| | | registry.addInterceptor(currentUserInterceptor).addPathPatterns("/api/**").order(0); |
| | | registry.addInterceptor(operationLogInterceptor).addPathPatterns("/api/**"); |
| | | } |
| | | |
| | | @Override |
| | | public void addCorsMappings(CorsRegistry registry) { |
| | | registry.addMapping("/api/**") |
| | |
| | | package com.emergencyrepair.system.controller; |
| | | |
| | | import com.emergencyrepair.business.dto.Requests; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.emergencyrepair.business.entity.BaseEquipment; |
| | | import com.emergencyrepair.business.entity.Contractor; |
| | | import com.emergencyrepair.business.entity.EngineeringMajor; |
| | | import com.emergencyrepair.business.entity.ProductNode; |
| | | import com.emergencyrepair.business.entity.TeamMajor; |
| | | import com.emergencyrepair.common.Result; |
| | | import com.emergencyrepair.system.entity.SysMenu; |
| | | import com.emergencyrepair.system.entity.SysLoginLog; |
| | | import com.emergencyrepair.system.entity.SysOperationLog; |
| | | import com.emergencyrepair.system.entity.SysRole; |
| | | import com.emergencyrepair.system.service.SystemService; |
| | | import lombok.RequiredArgsConstructor; |
| | |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RequestHeader; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | |
| | | private final SystemService systemService; |
| | | |
| | | @GetMapping("/menu/nav") |
| | | public Result<List<SysMenu>> nav() { |
| | | return Result.ok(systemService.menuTree()); |
| | | public Result<List<SysMenu>> nav(@RequestHeader(value = "X-Username", required = false) String username) { |
| | | return Result.ok(systemService.menuTreeForUser(username)); |
| | | } |
| | | |
| | | @GetMapping("/menu/list") |
| | | public Result<List<SysMenu>> menuList() { |
| | | return Result.ok(systemService.listMenus()); |
| | | } |
| | | |
| | | @GetMapping("/menu/tree") |
| | | public Result<List<SysMenu>> menuTreeWithButtons() { |
| | | return Result.ok(systemService.menuTreeWithButtons()); |
| | | } |
| | | |
| | | @GetMapping("/menu/buttons") |
| | | public Result<Map<String, Object>> menuButtons(@RequestParam Long menuId) { |
| | | return Result.ok(systemService.menuButtons(menuId)); |
| | | } |
| | | |
| | | @PostMapping("/menu/buttons") |
| | | public Result<Void> syncMenuButtons(@RequestBody Requests.ButtonSyncRequest request) { |
| | | systemService.syncMenuButtons(request.getMenuId(), request.getCodes()); |
| | | return Result.ok(); |
| | | } |
| | | |
| | | @GetMapping("/users") |
| | | public Result<Map<String, Object>> users(@RequestParam(required = false) String keyword, |
| | | @RequestParam(required = false) Long deptId, |
| | | @RequestParam(required = false) Long roleId, |
| | | @RequestParam(defaultValue = "1") int current, |
| | | @RequestParam(defaultValue = "20") int size) { |
| | | return Result.ok(systemService.userPage(keyword, deptId, roleId, current, size)); |
| | | } |
| | | |
| | | @GetMapping("/users/{userId}/roles") |
| | | public Result<List<String>> userRoles(@PathVariable Long userId) { |
| | | return Result.ok(systemService.userRoleIds(userId)); |
| | | } |
| | | |
| | | @PutMapping("/users/{userId}/roles") |
| | | public Result<Void> saveUserRoles(@PathVariable Long userId, @RequestBody Requests.UserRoleRequest request) { |
| | | systemService.saveUserRoles(userId, request.getRoleIds()); |
| | | return Result.ok(); |
| | | } |
| | | |
| | | @PutMapping("/users/roles/batch") |
| | | public Result<Void> saveBatchUserRoles(@RequestBody Requests.BatchUserRoleRequest request) { |
| | | systemService.saveUserRolesBatch(request.getUserIds(), request.getRoleIds(), request.getMode()); |
| | | return Result.ok(); |
| | | } |
| | | |
| | | @GetMapping("/depts") |
| | | public Result<List<Map<String, Object>>> depts() { |
| | | return Result.ok(systemService.depts()); |
| | | } |
| | | |
| | | @GetMapping("/login-logs") |
| | | public Result<Page<SysLoginLog>> loginLogs(@RequestParam(defaultValue = "1") int current, |
| | | @RequestParam(defaultValue = "20") int size, |
| | | @RequestParam(required = false) String keyword, |
| | | @RequestParam(required = false) String status, |
| | | @RequestParam(required = false) String startDate, |
| | | @RequestParam(required = false) String endDate) { |
| | | return Result.ok(systemService.loginLogPage(current, size, keyword, status, startDate, endDate)); |
| | | } |
| | | |
| | | @GetMapping("/operation-logs") |
| | | public Result<Page<SysOperationLog>> operationLogs(@RequestParam(defaultValue = "1") int current, |
| | | @RequestParam(defaultValue = "20") int size, |
| | | @RequestParam(required = false) String keyword, |
| | | @RequestParam(required = false) String module, |
| | | @RequestParam(required = false) String operationType, |
| | | @RequestParam(required = false) String startDate, |
| | | @RequestParam(required = false) String endDate) { |
| | | return Result.ok(systemService.operationLogPage(current, size, keyword, module, operationType, startDate, endDate)); |
| | | } |
| | | |
| | | @PostMapping("/menu") |
| | |
| | | return Result.ok(); |
| | | } |
| | | |
| | | @GetMapping("/contractors") |
| | | public Result<List<Contractor>> contractors() { |
| | | return Result.ok(systemService.listContractors()); |
| | | @GetMapping("/roles/{roleId}/users") |
| | | public Result<Map<String, Object>> roleUsers(@PathVariable Long roleId) { |
| | | return Result.ok(systemService.roleUsers(roleId)); |
| | | } |
| | | |
| | | @PostMapping("/contractors") |
| | | public Result<Contractor> createContractor(@RequestBody Contractor contractor) { |
| | | contractor.setId(null); |
| | | return Result.ok(systemService.saveContractor(contractor)); |
| | | } |
| | | |
| | | @PutMapping("/contractors/{id}") |
| | | public Result<Contractor> updateContractor(@PathVariable Long id, @RequestBody Contractor contractor) { |
| | | contractor.setId(id); |
| | | return Result.ok(systemService.saveContractor(contractor)); |
| | | } |
| | | |
| | | @DeleteMapping("/contractors/{id}") |
| | | public Result<Void> deleteContractor(@PathVariable Long id) { |
| | | systemService.deleteContractor(id); |
| | | @PutMapping("/roles/{roleId}/users") |
| | | public Result<Void> saveRoleUsers(@PathVariable Long roleId, @RequestBody Requests.RoleUserRequest request) { |
| | | systemService.saveRoleUsers(roleId, request.getUserIds()); |
| | | return Result.ok(); |
| | | } |
| | | |
| | | @GetMapping("/contractors") |
| | | public Result<List<Map<String, Object>>> contractors() { |
| | | return Result.ok(systemService.listContractors()); |
| | | } |
| | | |
| | | @GetMapping("/engineering-majors") |
| | |
| | | return Result.ok(systemService.tree(type)); |
| | | } |
| | | |
| | | @GetMapping("/tree/product/roots") |
| | | public Result<List<Map<String, Object>>> productTreeRoots() { |
| | | return Result.ok(systemService.productTreeRoots()); |
| | | } |
| | | |
| | | @GetMapping("/tree/product/children") |
| | | public Result<List<Map<String, Object>>> productTreeChildren(@RequestParam Long parentId) { |
| | | return Result.ok(systemService.productTreeChildren(parentId)); |
| | | } |
| | | |
| | | @GetMapping("/tree/product/side-roots") |
| | | public Result<List<Map<String, Object>>> productTreeForXNo(@RequestParam(required = false) String xNo) { |
| | | return Result.ok(systemService.productTreeForXNo(xNo)); |
| | | } |
| | | |
| | | @GetMapping("/tree/product/x-numbers") |
| | | public Result<List<String>> productXNumbers() { |
| | | return Result.ok(systemService.productXNumbers()); |
| | | } |
| | | |
| | | @GetMapping("/tree/product/selected") |
| | | public Result<List<Map<String, Object>>> selectedProductNodes(@RequestParam List<Long> ids) { |
| | | return Result.ok(systemService.selectedProductNodes(ids)); |
| | | } |
| | | |
| | | @PostMapping("/tree/product") |
| | | public Result<ProductNode> createProduct(@RequestBody ProductNode node) { |
| | | node.setId(null); |
| New file |
| | |
| | | package com.emergencyrepair.system.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| | | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | @TableName("LX.T_SYS_LOGIN_LOG") |
| | | public class SysLoginLog extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | @TableField("USER_ID") private Long userId; |
| | | @TableField("USER_NAME") private String userName; |
| | | @TableField("NICK_NAME") private String nickName; |
| | | @TableField("DEPT_NAME") private String deptName; |
| | | @TableField("LOGIN_IP") private String loginIp; |
| | | @TableField("STATUS") private String status; |
| | | @TableField("MESSAGE") private String message; |
| | | @TableField("LOGIN_TIME") private LocalDateTime loginTime; |
| | | } |
| | |
| | | package com.emergencyrepair.system.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_SYS_MENU") |
| | | public class SysMenu { |
| | | public class SysMenu extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| New file |
| | |
| | | package com.emergencyrepair.system.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| | | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | @TableName("LX.T_SYS_OPERATION_LOG") |
| | | public class SysOperationLog extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | @TableField("USER_ID") private Long userId; |
| | | @TableField("USER_NAME") private String userName; |
| | | @TableField("NICK_NAME") private String nickName; |
| | | @TableField("MODULE") private String module; |
| | | @TableField("OPERATION_TYPE") private String operationType; |
| | | @TableField("TARGET_TYPE") private String targetType; |
| | | @TableField("TARGET_ID") private String targetId; |
| | | @TableField("DESCRIPTION") private String description; |
| | | @TableField("REQUEST_METHOD") private String requestMethod; |
| | | @TableField("REQUEST_URI") private String requestUri; |
| | | @TableField("RESULT") private String result; |
| | | @TableField("CREATE_TIME") private LocalDateTime createTime; |
| | | } |
| | |
| | | package com.emergencyrepair.system.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_SYS_ROLE") |
| | | public class SysRole { |
| | | public class SysRole extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | package com.emergencyrepair.system.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_SYS_ROLE_MENU") |
| | | public class SysRoleMenu { |
| | | public class SysRoleMenu extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | package com.emergencyrepair.system.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_SYS_ROLE_USER") |
| | | public class SysRoleUser { |
| | | public class SysRoleUser extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | |
| | | package com.emergencyrepair.system.entity; |
| | | |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | |
| | | @Data |
| | | @TableName("LX.T_SYS_TENANT_MENU") |
| | | public class SysTenantMenu { |
| | | public class SysTenantMenu extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| New file |
| | |
| | | package com.emergencyrepair.system.log; |
| | | |
| | | import com.emergencyrepair.system.entity.SysOperationLog; |
| | | import com.emergencyrepair.common.audit.CurrentUserContext; |
| | | import com.emergencyrepair.system.mapper.SysOperationLogMapper; |
| | | import com.emergencyrepair.system.service.SystemService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.jdbc.core.JdbcTemplate; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.servlet.HandlerInterceptor; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | |
| | | /** |
| | | * Records add/update/delete calls. Login is covered by the login log. |
| | | */ |
| | | @Component |
| | | @RequiredArgsConstructor |
| | | public class OperationLogInterceptor implements HandlerInterceptor { |
| | | private static final Pattern TARGET_ID = Pattern.compile(".*/(\\d{6,})(?:/.*)?$"); |
| | | |
| | | private final SysOperationLogMapper operationLogMapper; |
| | | private final SystemService systemService; |
| | | private final JdbcTemplate jdbcTemplate; |
| | | |
| | | @Override |
| | | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, |
| | | Object handler, Exception exception) { |
| | | String method = request.getMethod(); |
| | | if (!"POST".equals(method) && !"PUT".equals(method) && !"DELETE".equals(method)) { |
| | | return; |
| | | } |
| | | String uri = request.getRequestURI(); |
| | | if (!StringUtils.hasText(uri) || !uri.startsWith("/api/") || uri.startsWith("/api/auth/login")) { |
| | | return; |
| | | } |
| | | try { |
| | | String username = CurrentUserContext.get(); |
| | | SysOperationLog log = new SysOperationLog(); |
| | | log.setUserName(username); |
| | | Long userId = systemService.resolveUserId(username); |
| | | log.setUserId(userId); |
| | | log.setNickName(resolveNickName(userId)); |
| | | log.setModule(resolveModule(uri)); |
| | | log.setOperationType(resolveOperationType(uri, method)); |
| | | log.setTargetType(resolveTargetType(uri)); |
| | | log.setTargetId(resolveTargetId(uri)); |
| | | log.setDescription(method + " " + uri); |
| | | log.setRequestMethod(method); |
| | | log.setRequestUri(uri); |
| | | log.setResult(exception == null && response.getStatus() < 400 ? "\u6210\u529f" : "\u5931\u8d25"); |
| | | log.setCreateTime(LocalDateTime.now()); |
| | | operationLogMapper.insert(log); |
| | | } catch (Exception ignored) { |
| | | // Logging must never break the business request. |
| | | } |
| | | } |
| | | |
| | | private String resolveNickName(Long userId) { |
| | | if (userId == null) { |
| | | return null; |
| | | } |
| | | List<String> names = jdbcTemplate.query( |
| | | "SELECT NICK_NAME FROM CSICZB_ZB.SYS_USER WHERE USER_ID = ?", |
| | | new Object[]{userId}, (rs, rowNum) -> rs.getString(1)); |
| | | return names.isEmpty() ? null : names.get(0); |
| | | } |
| | | |
| | | private String resolveModule(String uri) { |
| | | if (uri.startsWith("/api/faults") || uri.startsWith("/api/fault")) return "\u6545\u969c\u7ba1\u7406"; |
| | | if (uri.startsWith("/api/work-orders") || uri.startsWith("/api/completion")) return "\u5b8c\u5de5\u7ba1\u7406"; |
| | | if (uri.startsWith("/api/acceptances")) return "\u5b8c\u5de5\u9a8c\u6536"; |
| | | if (uri.startsWith("/api/reports")) return "\u7edf\u8ba1\u62a5\u8868"; |
| | | if (uri.startsWith("/api/system/users")) return "\u7528\u6237\u7ba1\u7406"; |
| | | if (uri.startsWith("/api/system/roles")) return "\u89d2\u8272\u7ba1\u7406"; |
| | | if (uri.startsWith("/api/system/menu")) return "\u83dc\u5355\u7ba1\u7406"; |
| | | if (uri.startsWith("/api/system/contractors")) return "\u627f\u4fee\u5355\u4f4d\u7ba1\u7406"; |
| | | if (uri.startsWith("/api/system")) return "\u7cfb\u7edf\u7ba1\u7406"; |
| | | return "\u5176\u4ed6"; |
| | | } |
| | | |
| | | private String resolveOperationType(String uri, String method) { |
| | | if (uri.contains("/dispatch")) return "\u6d3e\u5355"; |
| | | if (uri.contains("/approve") || uri.contains("/approval") || uri.contains("/review")) return "\u5ba1\u6279"; |
| | | if (uri.contains("/cancel")) return "\u9500\u9879"; |
| | | if (uri.contains("/submit")) return "\u63d0\u4ea4"; |
| | | if (uri.contains("/apply")) return "\u7533\u8bf7"; |
| | | if (uri.contains("/print")) return "\u6253\u5370"; |
| | | if (uri.contains("/rating")) return "\u8bc4\u7ea7"; |
| | | if (uri.contains("/roles") || uri.contains("/menu-permissions")) return "\u6388\u6743"; |
| | | if (uri.contains("/buttons")) return "\u83dc\u5355\u6309\u94ae\u914d\u7f6e"; |
| | | if ("POST".equals(method)) return "\u65b0\u589e"; |
| | | if ("PUT".equals(method)) return "\u4fee\u6539"; |
| | | return "\u5220\u9664"; |
| | | } |
| | | |
| | | private String resolveTargetType(String uri) { |
| | | String[] parts = uri.replace("/api/", "").split("/"); |
| | | return parts.length == 0 ? "" : parts[0]; |
| | | } |
| | | |
| | | private String resolveTargetId(String uri) { |
| | | Matcher matcher = TARGET_ID.matcher(uri); |
| | | return matcher.matches() ? matcher.group(1) : null; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.emergencyrepair.system.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.emergencyrepair.system.entity.SysLoginLog; |
| | | |
| | | public interface SysLoginLogMapper extends BaseMapper<SysLoginLog> { |
| | | } |
| New file |
| | |
| | | package com.emergencyrepair.system.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.emergencyrepair.system.entity.SysOperationLog; |
| | | |
| | | public interface SysOperationLogMapper extends BaseMapper<SysOperationLog> { |
| | | } |
| New file |
| | |
| | | package com.emergencyrepair.system.service; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * Declarative catalog of the operation buttons each page exposes. |
| | | * Menu management uses it to create MENU_TYPE=2 entries automatically. |
| | | */ |
| | | @Component |
| | | public class ButtonPermissionCatalog { |
| | | @Data |
| | | @AllArgsConstructor |
| | | public static class ButtonDefinition { |
| | | private String code; |
| | | private String name; |
| | | } |
| | | |
| | | private static final Map<String, List<ButtonDefinition>> CATALOG = new LinkedHashMap<>(); |
| | | |
| | | static { |
| | | put("/dashboard", |
| | | def("dashboard:query", "\u67e5\u8be2")); |
| | | put("/fault/report", |
| | | def("fault:report:query", "\u67e5\u8be2"), |
| | | def("fault:report:add", "\u65b0\u589e"), |
| | | def("fault:report:edit", "\u4fee\u6539"), |
| | | def("fault:report:delete", "\u5220\u9664"), |
| | | def("fault:report:export", "\u5bfc\u51fa"), |
| | | def("fault:report:report", "\u4e0a\u62a5")); |
| | | put("/fault/review", |
| | | def("fault:review:query", "\u67e5\u8be2"), |
| | | def("fault:review:audit", "\u5ba1\u6838"), |
| | | def("fault:review:edit", "\u4fee\u6539"), |
| | | def("fault:review:cancel", "\u9500\u9879")); |
| | | put("/fault/approval/cmb", |
| | | def("fault:approval:query", "\u67e5\u8be2"), |
| | | def("fault:approval:approve", "\u5ba1\u6279")); |
| | | put("/fault/approval/jcc-assistant", |
| | | def("fault:approval:query", "\u67e5\u8be2"), |
| | | def("fault:approval:approve", "\u5ba1\u6279")); |
| | | put("/fault/approval/jcc-director", |
| | | def("fault:approval:query", "\u67e5\u8be2"), |
| | | def("fault:approval:approve", "\u5ba1\u6279")); |
| | | put("/fault/dispatch", |
| | | def("fault:dispatch:query", "\u67e5\u8be2"), |
| | | def("fault:dispatch:dispatch", "\u4e00\u952e\u6d3e\u5355")); |
| | | put("/fault/completion", |
| | | 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("/completion/report", |
| | | def("completion:report:save", "\u4fdd\u5b58"), |
| | | def("completion:report:submit", "\u63d0\u4ea4")); |
| | | put("/acceptance/application", |
| | | def("acceptance:application:query", "\u67e5\u8be2"), |
| | | def("acceptance:application:apply", "\u7533\u8bf7")); |
| | | put("/acceptance/inspector", |
| | | def("acceptance:inspector:query", "\u67e5\u8be2"), |
| | | def("acceptance:inspector:pass", "\u901a\u8fc7"), |
| | | def("acceptance:inspector:reject", "\u6253\u56de")); |
| | | put("/acceptance/leader", |
| | | def("acceptance:leader:query", "\u67e5\u8be2"), |
| | | def("acceptance:leader:pass", "\u901a\u8fc7"), |
| | | def("acceptance:leader:reject", "\u6253\u56de")); |
| | | put("/acceptance/print", |
| | | def("acceptance:print:query", "\u67e5\u8be2"), |
| | | def("acceptance:print:print", "\u6253\u5370")); |
| | | put("/acceptance/rating", |
| | | def("acceptance:rating:query", "\u67e5\u8be2"), |
| | | def("acceptance:rating:grade", "\u8bc4\u7ea7")); |
| | | put("/reports/overview", def("report:common:query", "\u67e5\u8be2"), def("report:common:export", "\u5bfc\u51fa")); |
| | | put("/reports/top-equipment", def("report:common:query", "\u67e5\u8be2"), def("report:common:export", "\u5bfc\u51fa")); |
| | | put("/reports/by-team", def("report:common:query", "\u67e5\u8be2"), def("report:common:export", "\u5bfc\u51fa")); |
| | | put("/reports/contractors", def("report:common:query", "\u67e5\u8be2"), def("report:common:export", "\u5bfc\u51fa")); |
| | | put("/reports/trend", def("report:common:query", "\u67e5\u8be2"), def("report:common:export", "\u5bfc\u51fa")); |
| | | put("/system/product-tree", def("system:tree:add", "\u65b0\u589e"), def("system:tree:edit", "\u7f16\u8f91"), def("system:tree:delete", "\u5220\u9664")); |
| | | put("/system/base-equipment", def("system:tree:add", "\u65b0\u589e"), def("system:tree:edit", "\u7f16\u8f91"), def("system:tree:delete", "\u5220\u9664")); |
| | | put("/system/engineering-majors", def("system:major:add", "\u65b0\u589e"), def("system:major:edit", "\u7f16\u8f91"), def("system:major:delete", "\u5220\u9664")); |
| | | put("/system/team-majors", def("system:major:add", "\u65b0\u589e"), def("system:major:edit", "\u7f16\u8f91"), def("system:major:delete", "\u5220\u9664")); |
| | | put("/system/users", def("system:users:query", "\u67e5\u8be2"), def("system:users:assign", "\u89d2\u8272\u6388\u6743")); |
| | | put("/system/roles", def("system:roles:add", "\u65b0\u589e"), def("system:roles:edit", "\u7f16\u8f91"), def("system:roles:delete", "\u5220\u9664")); |
| | | put("/system/roles", def("system:roles:add", "\u65b0\u589e"), def("system:roles:edit", "\u7f16\u8f91"), def("system:roles:delete", "\u5220\u9664"), def("system:roles:assign", "\u7528\u6237\u8bbe\u7f6e")); |
| | | put("/system/menus", def("system:menus:add", "\u65b0\u589e"), def("system:menus:edit", "\u7f16\u8f91"), def("system:menus:delete", "\u5220\u9664"), def("system:menus:buttons", "\u83dc\u5355\u6309\u94ae\u540c\u6b65")); |
| | | put("/system/menu-permissions", def("system:menu-permissions:save", "\u4fdd\u5b58\u6388\u6743")); |
| | | put("/system/login-logs", def("system:login-logs:query", "\u67e5\u8be2"), def("system:login-logs:export", "\u5bfc\u51fa")); |
| | | put("/system/operation-logs", def("system:operation-logs:query", "\u67e5\u8be2"), def("system:operation-logs:export", "\u5bfc\u51fa")); |
| | | } |
| | | |
| | | private static void put(String url, ButtonDefinition... definitions) { |
| | | List<ButtonDefinition> list = new ArrayList<>(); |
| | | Collections.addAll(list, definitions); |
| | | CATALOG.put(url, Collections.unmodifiableList(list)); |
| | | } |
| | | |
| | | private static ButtonDefinition def(String code, String name) { |
| | | return new ButtonDefinition(code, name); |
| | | } |
| | | |
| | | public List<ButtonDefinition> buttonsFor(String url) { |
| | | if (url == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return CATALOG.getOrDefault(url, Collections.emptyList()); |
| | | } |
| | | |
| | | public Map<String, List<ButtonDefinition>> catalog() { |
| | | return CATALOG; |
| | | } |
| | | } |
| | |
| | | package com.emergencyrepair.system.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.IdWorker; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.emergencyrepair.business.entity.BaseEquipment; |
| | | import com.emergencyrepair.business.entity.Contractor; |
| | | import com.emergencyrepair.business.entity.EngineeringMajor; |
| | | import com.emergencyrepair.business.entity.TeamMajor; |
| | | import com.emergencyrepair.business.entity.ProductNode; |
| | | import com.emergencyrepair.business.mapper.BaseEquipmentMapper; |
| | | import com.emergencyrepair.business.mapper.ContractorMapper; |
| | | import com.emergencyrepair.business.mapper.EngineeringMajorMapper; |
| | | import com.emergencyrepair.business.mapper.ProductNodeMapper; |
| | | import com.emergencyrepair.business.mapper.TeamMajorMapper; |
| | | import com.emergencyrepair.common.BusinessException; |
| | | import com.emergencyrepair.system.entity.SysMenu; |
| | | import com.emergencyrepair.system.entity.SysLoginLog; |
| | | import com.emergencyrepair.system.entity.SysOperationLog; |
| | | import com.emergencyrepair.system.entity.SysRole; |
| | | import com.emergencyrepair.system.entity.SysRoleMenu; |
| | | import com.emergencyrepair.system.entity.SysRoleUser; |
| | | import com.emergencyrepair.system.entity.SysTenantMenu; |
| | | import com.emergencyrepair.system.mapper.SysMenuMapper; |
| | | import com.emergencyrepair.system.mapper.SysLoginLogMapper; |
| | | import com.emergencyrepair.system.mapper.SysOperationLogMapper; |
| | | import com.emergencyrepair.system.mapper.SysRoleMapper; |
| | | import com.emergencyrepair.system.mapper.SysRoleMenuMapper; |
| | | import com.emergencyrepair.system.mapper.SysRoleUserMapper; |
| | | import com.emergencyrepair.system.mapper.SysTenantMenuMapper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.jdbc.core.JdbcTemplate; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalDate; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Collection; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | |
| | | private final SysRoleMenuMapper roleMenuMapper; |
| | | private final SysTenantMenuMapper tenantMenuMapper; |
| | | private final SysRoleUserMapper roleUserMapper; |
| | | private final ContractorMapper contractorMapper; |
| | | private final SysLoginLogMapper loginLogMapper; |
| | | private final SysOperationLogMapper operationLogMapper; |
| | | private final ButtonPermissionCatalog buttonPermissionCatalog; |
| | | private final JdbcTemplate jdbcTemplate; |
| | | private final EngineeringMajorMapper engineeringMajorMapper; |
| | | private final TeamMajorMapper teamMajorMapper; |
| | | private final ProductNodeMapper productNodeMapper; |
| | | private final BaseEquipmentMapper baseEquipmentMapper; |
| | | |
| | | public List<SysMenu> menuTree() { |
| | | List<SysMenu> menus = listMenus(); |
| | | // Buttons are permission points, not navigable nodes. |
| | | List<SysMenu> navigable = listMenus().stream() |
| | | .filter(menu -> menu.getMenuType() == null || menu.getMenuType() != 2) |
| | | .collect(Collectors.toList()); |
| | | return buildTree(navigable); |
| | | } |
| | | |
| | | public List<SysMenu> menuTreeWithButtons() { |
| | | return buildTree(listMenus()); |
| | | } |
| | | |
| | | private List<SysMenu> buildTree(List<SysMenu> menus) { |
| | | Map<Long, SysMenu> map = menus.stream().collect(Collectors.toMap(SysMenu::getId, item -> item)); |
| | | List<SysMenu> roots = new ArrayList<>(); |
| | | for (SysMenu menu : menus) { |
| | |
| | | public SysMenu saveMenu(SysMenu menu) { |
| | | if (!StringUtils.hasText(menu.getName())) { |
| | | throw new BusinessException("èååç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (menu.getMenuType() == null) { |
| | | menu.setMenuType(menu.getId() == null ? 1 : menu.getMenuType()); |
| | | } |
| | | boolean isRoot = menu.getPid() == null || menu.getPid() == 0L; |
| | | if (menu.getMenuType() == 2) { |
| | | if (isRoot) { |
| | | throw new BusinessException("æé®ç±»ååªè½å»ºå¨èåç±»åä¸çº§"); |
| | | } |
| | | SysMenu parent = menuMapper.selectById(menu.getPid()); |
| | | if (parent == null || parent.getMenuType() == null || parent.getMenuType() != 1) { |
| | | throw new BusinessException("æé®ç±»ååªè½å»ºå¨èåç±»åä¸çº§"); |
| | | } |
| | | if (!StringUtils.hasText(menu.getPermissions())) { |
| | | throw new BusinessException("æé®æéæ è¯ä¸è½ä¸ºç©º"); |
| | | } |
| | | menu.setUrl(null); |
| | | menu.setIcon(null); |
| | | } else if (!isRoot) { |
| | | SysMenu parent = menuMapper.selectById(menu.getPid()); |
| | | if (parent != null && parent.getMenuType() != null && parent.getMenuType() == 2) { |
| | | throw new BusinessException("æé®ç±»åä¸çº§ä¸è½å建èå"); |
| | | } |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | menu.setUpdateTime(now); |
| | |
| | | roleUserMapper.delete(Wrappers.<SysRoleUser>lambdaQuery().eq(SysRoleUser::getRoleId, id)); |
| | | } |
| | | |
| | | public List<Contractor> listContractors() { |
| | | return contractorMapper.selectList(Wrappers.<Contractor>lambdaQuery() |
| | | .orderByAsc(Contractor::getSortNo).orderByAsc(Contractor::getId)); |
| | | } |
| | | |
| | | @Transactional |
| | | public Contractor saveContractor(Contractor contractor) { |
| | | if (!StringUtils.hasText(contractor.getName())) { |
| | | throw new BusinessException("æ¿ä¿®åä½åç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | contractor.setUpdateTime(now); |
| | | if (contractor.getId() == null) { |
| | | contractor.setCreateTime(now); |
| | | contractorMapper.insert(contractor); |
| | | } else { |
| | | contractorMapper.updateById(contractor); |
| | | } |
| | | return contractorMapper.selectById(contractor.getId()); |
| | | } |
| | | |
| | | @Transactional |
| | | public void deleteContractor(Long id) { |
| | | contractorMapper.deleteById(id); |
| | | /** |
| | | * Contractor units are maintained as department records with DEPT_TYPE='cj'. |
| | | */ |
| | | public List<Map<String, Object>> listContractors() { |
| | | return jdbcTemplate.query( |
| | | "SELECT DEPT_ID, DEPT_NAME, ORDER_NUM, LEADER, PHONE, STATUS, DEPT_CODE FROM CSICZB_ZB.SYS_DEPT " |
| | | + "WHERE DEPT_TYPE = 'cj' AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL) ORDER BY ORDER_NUM, DEPT_ID", |
| | | (rs, rowNum) -> { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | row.put("id", String.valueOf(rs.getLong("DEPT_ID"))); |
| | | row.put("name", rs.getString("DEPT_NAME")); |
| | | row.put("shortName", null); |
| | | row.put("nature", "æ¿ä¿®åä½"); |
| | | row.put("sortNo", rs.getObject("ORDER_NUM")); |
| | | row.put("contact", rs.getString("LEADER")); |
| | | row.put("phone", rs.getString("PHONE")); |
| | | row.put("status", rs.getString("STATUS")); |
| | | row.put("deptCode", rs.getString("DEPT_CODE")); |
| | | row.put("dishonest", 0); |
| | | return row; |
| | | }); |
| | | } |
| | | |
| | | public List<EngineeringMajor> listEngineeringMajors() { |
| | |
| | | @Transactional |
| | | public TeamMajor saveTeamMajor(TeamMajor major) { |
| | | if (major == null || !StringUtils.hasText(major.getName())) { |
| | | throw new BusinessException("Téä¸ä¸åç§°ä¸è½ä¸ºç©º"); |
| | | throw new BusinessException("rk_Téä¸ä¸åç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | String name = major.getName().trim(); |
| | | String category = StringUtils.hasText(major.getCategory()) ? major.getCategory().trim().toUpperCase() : ""; |
| | | if (!"A".equals(category) && !"B".equals(category)) { |
| | | throw new BusinessException("Téä¸ä¸åç±»åªè½æ¯AæB"); |
| | | throw new BusinessException("rk_Téä¸ä¸åç±»åªè½æ¯AæB"); |
| | | } |
| | | LambdaQueryWrapper<TeamMajor> duplicate = Wrappers.<TeamMajor>lambdaQuery() |
| | | .eq(TeamMajor::getName, name); |
| | |
| | | duplicate.ne(TeamMajor::getId, major.getId()); |
| | | } |
| | | if (teamMajorMapper.selectCount(duplicate) > 0) { |
| | | throw new BusinessException("Téä¸ä¸åç§°å·²åå¨ï¼" + name); |
| | | throw new BusinessException("rk_Téä¸ä¸åç§°å·²åå¨ï¼" + name); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | major.setName(name); |
| | |
| | | throw new BusinessException("æªç¥æ ç±»å"); |
| | | } |
| | | |
| | | public List<Map<String, Object>> selectedProductNodes(List<Long> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<ProductNode> source = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .in(ProductNode::getId, ids)); |
| | | Map<Long, ProductNode> unique = uniqueProductNodes(source); |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | Set<Long> seen = new HashSet<>(); |
| | | for (Long id : ids) { |
| | | ProductNode node = unique.get(id); |
| | | if (node == null || !seen.add(id)) { |
| | | continue; |
| | | } |
| | | List<ProductNode> path = productPath(node); |
| | | Map<String, Object> row = mapProduct(node, Collections.emptySet()); |
| | | row.put("pathNames", path.stream().map(ProductNode::getName).collect(Collectors.toList())); |
| | | row.put("parentPathNames", path.stream() |
| | | .filter(item -> item != node) |
| | | .filter(item -> item.getNodeLevel() != null && item.getNodeLevel() > 2) |
| | | .map(ProductNode::getName) |
| | | .collect(Collectors.toList())); |
| | | ProductNode xNode = path.stream() |
| | | .filter(item -> item.getNodeLevel() != null && item.getNodeLevel() == 2) |
| | | .findFirst().orElse(null); |
| | | row.put("xNo", xNode == null ? null : xNode.getName()); |
| | | result.add(row); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private List<Map<String, Object>> mapProductTreeNodes(List<ProductNode> source, Set<Long> forcedBranchIds) { |
| | | Map<Long, ProductNode> unique = uniqueProductNodes(source); |
| | | List<ProductNode> nodes = sortProductNodes(unique.values()); |
| | | Set<Long> branchIds = new HashSet<>(forcedBranchIds); |
| | | if (!unique.isEmpty()) { |
| | | List<ProductNode> childRows = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .select(ProductNode::getParentId, ProductNode::getIsDelete) |
| | | .in(ProductNode::getParentId, unique.keySet())); |
| | | for (ProductNode child : childRows) { |
| | | if (!"1".equals(child.getIsDelete()) && child.getParentId() != null) { |
| | | branchIds.add(child.getParentId()); |
| | | } |
| | | } |
| | | } |
| | | List<Map<String, Object>> rows = new ArrayList<>(); |
| | | for (ProductNode node : nodes) { |
| | | rows.add(mapProduct(node, branchIds)); |
| | | } |
| | | return rows; |
| | | } |
| | | |
| | | private Map<Long, ProductNode> uniqueProductNodes(Collection<ProductNode> source) { |
| | | Map<Long, ProductNode> unique = new LinkedHashMap<>(); |
| | | if (source == null) { |
| | | return unique; |
| | | } |
| | | for (ProductNode node : source) { |
| | | if (node.getId() == null || "1".equals(node.getIsDelete())) { |
| | | continue; |
| | | } |
| | | unique.putIfAbsent(node.getId(), node); |
| | | } |
| | | return unique; |
| | | } |
| | | |
| | | private List<ProductNode> sortProductNodes(Collection<ProductNode> source) { |
| | | List<ProductNode> nodes = new ArrayList<>(source); |
| | | nodes.sort(Comparator.comparingInt(this::productNodeLevel) |
| | | .thenComparing(ProductNode::getSortNo, Comparator.nullsLast(Integer::compareTo)) |
| | | .thenComparing(ProductNode::getId, Comparator.nullsLast(Long::compareTo))); |
| | | return nodes; |
| | | } |
| | | |
| | | private ProductNode findProductRoot(ProductNode node) { |
| | | ProductNode current = node; |
| | | Set<Long> visited = new HashSet<>(); |
| | | while (current != null && current.getParentId() != null && current.getParentId() != 0L |
| | | && visited.add(current.getId())) { |
| | | current = findProductNode(current.getParentId()); |
| | | } |
| | | return current; |
| | | } |
| | | |
| | | private List<ProductNode> productPath(ProductNode node) { |
| | | List<ProductNode> reversed = new ArrayList<>(); |
| | | Set<Long> visited = new HashSet<>(); |
| | | ProductNode current = node; |
| | | while (current != null && visited.add(current.getId())) { |
| | | reversed.add(current); |
| | | if (current.getParentId() == null || current.getParentId() == 0L) { |
| | | break; |
| | | } |
| | | current = findProductNode(current.getParentId()); |
| | | } |
| | | Collections.reverse(reversed); |
| | | return reversed; |
| | | } |
| | | |
| | | public List<Map<String, Object>> productTreeForXNo(String xNo) { |
| | | if (!StringUtils.hasText(xNo)) { |
| | | return productTreeRoots(); |
| | | } |
| | | List<ProductNode> rootNodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .and(wrapper -> wrapper.isNull(ProductNode::getParentId).or().eq(ProductNode::getParentId, 0L)) |
| | | .orderByAsc(ProductNode::getSortNo).orderByAsc(ProductNode::getId)); |
| | | List<ProductNode> sideNodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .in(ProductNode::getLayer, Arrays.asList("side", "SIDE")) |
| | | .eq(ProductNode::getName, xNo.trim()) |
| | | .orderByAsc(ProductNode::getSortNo).orderByAsc(ProductNode::getId)); |
| | | Map<Long, ProductNode> roots = uniqueProductNodes(rootNodes); |
| | | Map<Long, ProductNode> sides = uniqueProductNodes(sideNodes); |
| | | Set<Long> branchIds = new HashSet<>(); |
| | | branchIds.addAll(roots.keySet()); |
| | | branchIds.addAll(sides.keySet()); |
| | | Map<Long, Map<String, Object>> rootRows = new LinkedHashMap<>(); |
| | | for (ProductNode root : sortProductNodes(roots.values())) { |
| | | rootRows.put(root.getId(), mapProduct(root, branchIds)); |
| | | } |
| | | for (ProductNode side : sortProductNodes(sides.values())) { |
| | | ProductNode root = findProductRoot(side); |
| | | Map<String, Object> rootRow = root == null ? null : rootRows.get(root.getId()); |
| | | if (rootRow != null) { |
| | | children(rootRow).add(mapProduct(side, branchIds)); |
| | | } |
| | | } |
| | | return new ArrayList<>(rootRows.values()); |
| | | } |
| | | |
| | | public List<String> productXNumbers() { |
| | | List<ProductNode> nodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .in(ProductNode::getLayer, Arrays.asList("side", "SIDE")) |
| | | .orderByAsc(ProductNode::getSortNo).orderByAsc(ProductNode::getId)); |
| | | return nodes.stream() |
| | | .filter(node -> !"1".equals(node.getIsDelete())) |
| | | .map(ProductNode::getName) |
| | | .filter(StringUtils::hasText) |
| | | .map(String::trim) |
| | | .distinct() |
| | | .sorted(String.CASE_INSENSITIVE_ORDER) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | public List<Map<String, Object>> productTreeRoots() { |
| | | List<ProductNode> nodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .and(wrapper -> wrapper.isNull(ProductNode::getParentId).or().eq(ProductNode::getParentId, 0L)) |
| | | .orderByAsc(ProductNode::getSortNo).orderByAsc(ProductNode::getId)); |
| | | return mapProductTreeNodes(nodes, Collections.emptySet()); |
| | | } |
| | | |
| | | public List<Map<String, Object>> productTreeChildren(Long parentId) { |
| | | if (parentId == null || parentId == 0L) { |
| | | return productTreeRoots(); |
| | | } |
| | | List<ProductNode> nodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .eq(ProductNode::getParentId, parentId) |
| | | .orderByAsc(ProductNode::getSortNo).orderByAsc(ProductNode::getId)); |
| | | return mapProductTreeNodes(nodes, Collections.emptySet()); |
| | | } |
| | | |
| | | public List<Map<String, Object>> productTree() { |
| | | List<ProductNode> nodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .orderByAsc(ProductNode::getNodeLevel).orderByAsc(ProductNode::getSortNo)); |
| | | .orderByAsc(ProductNode::getId).orderByAsc(ProductNode::getSortNo)); |
| | | Map<Long, ProductNode> unique = new LinkedHashMap<>(); |
| | | for (ProductNode node : nodes) { |
| | | if (node.getId() == null || "1".equals(node.getIsDelete())) { |
| | | continue; |
| | | } |
| | | unique.putIfAbsent(node.getId(), node); |
| | | } |
| | | nodes = new ArrayList<>(unique.values()); |
| | | nodes.sort(Comparator.comparingInt(this::productNodeLevel) |
| | | .thenComparing(ProductNode::getSortNo, Comparator.nullsLast(Integer::compareTo)) |
| | | .thenComparing(ProductNode::getId, Comparator.nullsLast(Long::compareTo))); |
| | | Map<Long, Map<String, Object>> map = new LinkedHashMap<>(); |
| | | for (ProductNode node : nodes) { |
| | | map.put(node.getId(), mapProduct(node)); |
| | |
| | | |
| | | @Transactional |
| | | public ProductNode saveProductNode(ProductNode node) { |
| | | if (!StringUtils.hasText(node.getName()) || node.getNodeLevel() == null) { |
| | | throw new BusinessException("èç¹åç§°åå±çº§ä¸è½ä¸ºç©º"); |
| | | if (!StringUtils.hasText(node.getName())) { |
| | | throw new BusinessException("èç¹åç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | node.setUpdateTime(now); |
| | | String layer = resolveProductLayer(node); |
| | | node.setLayer(layer); |
| | | node.setConnectGroup(buildConnectGroup(node.getParentId())); |
| | | |
| | | if (node.getId() == null) { |
| | | node.setId(IdWorker.getId()); |
| | | node.setRecordId(String.valueOf(node.getId())); |
| | | node.setProject(StringUtils.hasText(node.getProject()) ? node.getProject().trim() : "0"); |
| | | node.setSortNo(node.getSortNo() == null ? 1 : node.getSortNo()); |
| | | node.setIsDelete("0"); |
| | | node.setCreateBy(StringUtils.hasText(node.getCreateBy()) ? node.getCreateBy().trim() : "system"); |
| | | node.setCreateTime(now); |
| | | productNodeMapper.insert(node); |
| | | } else { |
| | | ProductNode current = findProductNode(node.getId()); |
| | | if (current == null) { |
| | | throw new BusinessException("èç¹ä¸åå¨"); |
| | | } |
| | | node.setRecordId(current.getRecordId()); |
| | | node.setProject(StringUtils.hasText(current.getProject()) ? current.getProject() : "0"); |
| | | node.setIsDelete(StringUtils.hasText(current.getIsDelete()) ? current.getIsDelete() : "0"); |
| | | node.setCreateBy(current.getCreateBy()); |
| | | node.setCreateTime(current.getCreateTime()); |
| | | productNodeMapper.updateById(node); |
| | | } |
| | | return productNodeMapper.selectById(node.getId()); |
| | | return findProductNode(node.getId()); |
| | | } |
| | | |
| | | @Transactional |
| | | public void deleteProductNode(Long id) { |
| | | assertNoChildren(productNodeMapper.selectCount(Wrappers.<ProductNode>lambdaQuery().eq(ProductNode::getParentId, id))); |
| | | productNodeMapper.deleteById(id); |
| | | assertNoChildren(productNodeMapper.selectCount(Wrappers.<ProductNode>lambdaQuery() |
| | | .eq(ProductNode::getParentId, id).ne(ProductNode::getIsDelete, "1"))); |
| | | ProductNode current = findProductNode(id); |
| | | if (current == null) { |
| | | throw new BusinessException("èç¹ä¸åå¨"); |
| | | } |
| | | current.setIsDelete("1"); |
| | | productNodeMapper.updateById(current); |
| | | } |
| | | |
| | | @Transactional |
| | |
| | | } |
| | | roleMenuMapper.delete(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getRoleId, roleId)); |
| | | tenantMenuMapper.delete(Wrappers.<SysTenantMenu>lambdaQuery().eq(SysTenantMenu::getTenantId, tenantId)); |
| | | for (Long menuId : roleMenuIds == null ? Collections.<Long>emptyList() : roleMenuIds) { |
| | | List<Long> effectiveRoleMenuIds = expandWithAncestors(roleMenuIds); |
| | | List<Long> effectiveTenantMenuIds = expandWithAncestors(tenantMenuIds); |
| | | for (Long menuId : effectiveRoleMenuIds) { |
| | | SysRoleMenu relation = new SysRoleMenu(); |
| | | relation.setRoleId(roleId); |
| | | relation.setMenuId(menuId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | roleMenuMapper.insert(relation); |
| | | } |
| | | for (Long menuId : tenantMenuIds == null ? Collections.<Long>emptyList() : tenantMenuIds) { |
| | | for (Long menuId : effectiveTenantMenuIds) { |
| | | SysTenantMenu relation = new SysTenantMenu(); |
| | | relation.setTenantId(tenantId); |
| | | relation.setMenuId(menuId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | tenantMenuMapper.insert(relation); |
| | | } |
| | | } |
| | | |
| | | public Map<String, Object> userPage(String keyword, Long deptId, Long roleId, int current, int size) { |
| | | List<Object> args = new ArrayList<>(); |
| | | StringBuilder sql = new StringBuilder(); |
| | | sql.append("SELECT u.USER_ID, u.USER_NAME, u.NICK_NAME, u.DEPT_ID, d.DEPT_NAME, u.PHONENUMBER, u.STATUS, u.LOGIN_DATE "); |
| | | sql.append("FROM CSICZB_ZB.SYS_USER u LEFT JOIN CSICZB_ZB.SYS_DEPT d ON d.DEPT_ID = u.DEPT_ID "); |
| | | sql.append("WHERE (u.DEL_FLAG = '0' OR u.DEL_FLAG IS NULL) "); |
| | | if (StringUtils.hasText(keyword)) { |
| | | sql.append("AND (u.USER_NAME LIKE ? OR u.NICK_NAME LIKE ?) "); |
| | | String like = "%" + keyword.trim() + "%"; |
| | | args.add(like); |
| | | args.add(like); |
| | | } |
| | | if (deptId != null) { |
| | | sql.append("AND u.DEPT_ID = ? "); |
| | | args.add(deptId); |
| | | } |
| | | if (roleId != null) { |
| | | sql.append("AND EXISTS (SELECT 1 FROM LX.T_SYS_ROLE_USER ru WHERE ru.USER_ID = u.USER_ID AND ru.ROLE_ID = ? AND ru.DEL_FLAG = 0) "); |
| | | args.add(roleId); |
| | | } |
| | | sql.append("ORDER BY u.USER_ID"); |
| | | List<Map<String, Object>> all = jdbcTemplate.query(sql.toString(), args.toArray(), (rs, rowNum) -> { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | row.put("userId", String.valueOf(rs.getLong("USER_ID"))); |
| | | row.put("userName", rs.getString("USER_NAME")); |
| | | row.put("nickName", rs.getString("NICK_NAME")); |
| | | Object dept = rs.getObject("DEPT_ID"); |
| | | row.put("deptId", dept == null ? null : String.valueOf(rs.getLong("DEPT_ID"))); |
| | | row.put("deptName", rs.getString("DEPT_NAME")); |
| | | row.put("phoneNumber", rs.getString("PHONENUMBER")); |
| | | row.put("status", rs.getString("STATUS")); |
| | | java.sql.Timestamp loginDate = rs.getTimestamp("LOGIN_DATE"); |
| | | row.put("loginDate", loginDate == null ? null : loginDate.toLocalDateTime().toString().replace('T', ' ')); |
| | | row.put("roles", new ArrayList<Map<String, Object>>()); |
| | | return row; |
| | | }); |
| | | Map<Long, Map<String, Object>> byId = new HashMap<>(); |
| | | for (Map<String, Object> row : all) { |
| | | byId.put(Long.valueOf(String.valueOf(row.get("userId"))), row); |
| | | } |
| | | if (!byId.isEmpty()) { |
| | | List<Map<String, Object>> roleRows = jdbcTemplate.query( |
| | | "SELECT ru.USER_ID, r.ID, r.NAME, r.CODE, r.ROLE_TYPE FROM LX.T_SYS_ROLE_USER ru JOIN LX.T_SYS_ROLE r ON r.ID = ru.ROLE_ID AND r.DEL_FLAG = 0 WHERE ru.DEL_FLAG = 0 ORDER BY r.SORT_NO", |
| | | (rs, rowNum) -> { |
| | | Map<String, Object> role = new LinkedHashMap<>(); |
| | | role.put("userId", String.valueOf(rs.getLong("USER_ID"))); |
| | | role.put("roleId", String.valueOf(rs.getLong("ID"))); |
| | | role.put("roleName", rs.getString("NAME")); |
| | | role.put("roleCode", rs.getString("CODE")); |
| | | role.put("roleType", rs.getString("ROLE_TYPE")); |
| | | return role; |
| | | }); |
| | | for (Map<String, Object> role : roleRows) { |
| | | Map<String, Object> user = byId.get(Long.valueOf(String.valueOf(role.get("userId")))); |
| | | if (user != null) { |
| | | @SuppressWarnings("unchecked") |
| | | List<Map<String, Object>> roles = (List<Map<String, Object>>) user.get("roles"); |
| | | roles.add(role); |
| | | } |
| | | } |
| | | } |
| | | int safeSize = size <= 0 ? 20 : size; |
| | | int safeCurrent = current <= 0 ? 1 : current; |
| | | int total = all.size(); |
| | | int from = Math.min(total, (safeCurrent - 1) * safeSize); |
| | | int to = Math.min(total, from + safeSize); |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("records", new ArrayList<>(all.subList(from, to))); |
| | | result.put("total", total); |
| | | result.put("current", safeCurrent); |
| | | result.put("size", safeSize); |
| | | result.put("pages", (total + safeSize - 1) / safeSize); |
| | | return result; |
| | | } |
| | | |
| | | public List<Map<String, Object>> depts() { |
| | | return jdbcTemplate.query( |
| | | "SELECT DEPT_ID, DEPT_NAME, PARENT_ID, ORDER_NUM, DEPT_TYPE, LEADER, PHONE, STATUS, DEPT_CODE FROM CSICZB_ZB.SYS_DEPT WHERE (DEL_FLAG = '0' OR DEL_FLAG IS NULL) ORDER BY ORDER_NUM, DEPT_ID", |
| | | (rs, rowNum) -> { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | row.put("id", String.valueOf(rs.getLong("DEPT_ID"))); |
| | | row.put("name", rs.getString("DEPT_NAME")); |
| | | Object parentId = rs.getObject("PARENT_ID"); |
| | | row.put("parentId", parentId == null ? null : String.valueOf(rs.getLong("PARENT_ID"))); |
| | | row.put("orderNum", rs.getObject("ORDER_NUM")); |
| | | row.put("deptType", rs.getString("DEPT_TYPE")); |
| | | row.put("leader", rs.getString("LEADER")); |
| | | row.put("phone", rs.getString("PHONE")); |
| | | row.put("status", rs.getString("STATUS")); |
| | | row.put("deptCode", rs.getString("DEPT_CODE")); |
| | | return row; |
| | | }); |
| | | } |
| | | |
| | | public List<String> userRoleIds(Long userId) { |
| | | if (userId == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | return roleUserMapper.selectList(Wrappers.<SysRoleUser>lambdaQuery() |
| | | .eq(SysRoleUser::getUserId, userId)) |
| | | .stream().map(item -> String.valueOf(item.getRoleId())).collect(Collectors.toList()); |
| | | } |
| | | |
| | | public Map<String, Object> roleUsers(Long roleId) { |
| | | if (roleId == null || roleMapper.selectById(roleId) == null) { |
| | | throw new BusinessException("è§è²ä¸åå¨"); |
| | | } |
| | | Set<Long> assignedUserIds = roleUserMapper.selectList(Wrappers.<SysRoleUser>lambdaQuery() |
| | | .eq(SysRoleUser::getRoleId, roleId)) |
| | | .stream().map(SysRoleUser::getUserId).collect(Collectors.toCollection(LinkedHashSet::new)); |
| | | List<Map<String, Object>> users = jdbcTemplate.query( |
| | | "SELECT u.USER_ID, u.USER_NAME, u.NICK_NAME, u.DEPT_ID, d.DEPT_NAME, u.STATUS " + |
| | | "FROM CSICZB_ZB.SYS_USER u LEFT JOIN CSICZB_ZB.SYS_DEPT d ON d.DEPT_ID = u.DEPT_ID " + |
| | | "WHERE (u.DEL_FLAG = '0' OR u.DEL_FLAG IS NULL) ORDER BY u.USER_ID", |
| | | (rs, rowNum) -> { |
| | | Long userId = rs.getLong("USER_ID"); |
| | | Map<String, Object> user = new LinkedHashMap<>(); |
| | | user.put("userId", String.valueOf(userId)); |
| | | user.put("userName", rs.getString("USER_NAME")); |
| | | user.put("nickName", rs.getString("NICK_NAME")); |
| | | Object deptId = rs.getObject("DEPT_ID"); |
| | | user.put("deptId", deptId == null ? null : String.valueOf(rs.getLong("DEPT_ID"))); |
| | | user.put("deptName", rs.getString("DEPT_NAME")); |
| | | user.put("status", rs.getString("STATUS")); |
| | | user.put("assigned", assignedUserIds.contains(userId)); |
| | | return user; |
| | | }); |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("records", users); |
| | | result.put("userIds", assignedUserIds.stream().map(String::valueOf).collect(Collectors.toList())); |
| | | result.put("total", users.size()); |
| | | return result; |
| | | } |
| | | |
| | | @Transactional |
| | | public void saveRoleUsers(Long roleId, List<Long> userIds) { |
| | | if (roleId == null || roleMapper.selectById(roleId) == null) { |
| | | throw new BusinessException("è§è²ä¸åå¨"); |
| | | } |
| | | Set<Long> distinct = new LinkedHashSet<>(); |
| | | if (userIds != null) { |
| | | distinct.addAll(userIds); |
| | | } |
| | | distinct.remove(null); |
| | | for (Long userId : distinct) { |
| | | Integer exists = jdbcTemplate.queryForObject( |
| | | "SELECT COUNT(*) FROM CSICZB_ZB.SYS_USER WHERE USER_ID = ? AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL)", |
| | | Integer.class, userId); |
| | | if (exists == null || exists == 0) { |
| | | throw new BusinessException("ç¨æ·ä¸åå¨"); |
| | | } |
| | | } |
| | | roleUserMapper.delete(Wrappers.<SysRoleUser>lambdaQuery().eq(SysRoleUser::getRoleId, roleId)); |
| | | for (Long userId : distinct) { |
| | | SysRoleUser relation = new SysRoleUser(); |
| | | relation.setRoleId(roleId); |
| | | relation.setUserId(userId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | roleUserMapper.insert(relation); |
| | | } |
| | | } |
| | | |
| | | @Transactional |
| | | public void saveUserRoles(Long userId, List<Long> roleIds) { |
| | | if (userId == null) { |
| | | throw new BusinessException("è¯·éæ©ç¨æ·"); |
| | | } |
| | | Integer exists = jdbcTemplate.queryForObject( |
| | | "SELECT COUNT(*) FROM CSICZB_ZB.SYS_USER WHERE USER_ID = ? AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL)", |
| | | Integer.class, userId); |
| | | if (exists == null || exists == 0) { |
| | | throw new BusinessException("ç¨æ·ä¸åå¨"); |
| | | } |
| | | Set<Long> distinct = new LinkedHashSet<>(); |
| | | if (roleIds != null) { |
| | | distinct.addAll(roleIds); |
| | | } |
| | | for (Long roleId : distinct) { |
| | | if (roleId != null && roleMapper.selectById(roleId) == null) { |
| | | throw new BusinessException("è§è²ä¸åå¨"); |
| | | } |
| | | } |
| | | roleUserMapper.delete(Wrappers.<SysRoleUser>lambdaQuery().eq(SysRoleUser::getUserId, userId)); |
| | | for (Long roleId : distinct) { |
| | | if (roleId == null) { |
| | | continue; |
| | | } |
| | | SysRoleUser relation = new SysRoleUser(); |
| | | relation.setRoleId(roleId); |
| | | relation.setUserId(userId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | roleUserMapper.insert(relation); |
| | | } |
| | | } |
| | | |
| | | @Transactional |
| | | public void saveUserRolesBatch(List<Long> userIds, List<Long> roleIds, String mode) { |
| | | Set<Long> distinctUsers = new LinkedHashSet<>(); |
| | | if (userIds != null) { |
| | | distinctUsers.addAll(userIds); |
| | | } |
| | | distinctUsers.remove(null); |
| | | if (distinctUsers.isEmpty()) { |
| | | throw new BusinessException("\u8bf7\u9009\u62e9\u7528\u6237"); |
| | | } |
| | | Set<Long> distinctRoles = new LinkedHashSet<>(); |
| | | if (roleIds != null) { |
| | | distinctRoles.addAll(roleIds); |
| | | } |
| | | distinctRoles.remove(null); |
| | | for (Long roleId : distinctRoles) { |
| | | if (roleMapper.selectById(roleId) == null) { |
| | | throw new BusinessException("\u89d2\u8272\u4e0d\u5b58\u5728"); |
| | | } |
| | | } |
| | | boolean append = !"replace".equalsIgnoreCase(mode == null ? "" : mode.trim()); |
| | | for (Long userId : distinctUsers) { |
| | | Integer exists = jdbcTemplate.queryForObject( |
| | | "SELECT COUNT(*) FROM CSICZB_ZB.SYS_USER WHERE USER_ID = ? AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL)", |
| | | Integer.class, userId); |
| | | if (exists == null || exists == 0) { |
| | | throw new BusinessException("\u7528\u6237\u4e0d\u5b58\u5728"); |
| | | } |
| | | Set<Long> targetRoleIds = new LinkedHashSet<>(); |
| | | if (append) { |
| | | targetRoleIds.addAll(roleUserMapper.selectList(Wrappers.<SysRoleUser>lambdaQuery() |
| | | .eq(SysRoleUser::getUserId, userId)) |
| | | .stream().map(SysRoleUser::getRoleId).collect(Collectors.toList())); |
| | | } |
| | | targetRoleIds.addAll(distinctRoles); |
| | | roleUserMapper.delete(Wrappers.<SysRoleUser>lambdaQuery().eq(SysRoleUser::getUserId, userId)); |
| | | for (Long roleId : targetRoleIds) { |
| | | SysRoleUser relation = new SysRoleUser(); |
| | | relation.setRoleId(roleId); |
| | | relation.setUserId(userId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | roleUserMapper.insert(relation); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public List<String> roleCodes(String username) { |
| | | Long userId = resolveUserId(username); |
| | | if (userId == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | List<Long> roleIds = roleUserMapper.selectList(Wrappers.<SysRoleUser>lambdaQuery() |
| | | .eq(SysRoleUser::getUserId, userId)) |
| | | .stream().map(SysRoleUser::getRoleId).collect(Collectors.toList()); |
| | | if (roleIds.isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | | return roleMapper.selectBatchIds(roleIds).stream() |
| | | .map(SysRole::getCode).filter(StringUtils::hasText).sorted().collect(Collectors.toList()); |
| | | } |
| | | |
| | | public Set<Long> effectiveMenuIdsForUser(String username) { |
| | | Set<Long> result = new LinkedHashSet<>(); |
| | | if (!StringUtils.hasText(username)) { |
| | | return result; |
| | | } |
| | | if ("admin".equals(username)) { |
| | | listMenus().forEach(menu -> result.add(menu.getId())); |
| | | return result; |
| | | } |
| | | Long userId = resolveUserId(username); |
| | | if (userId == null) { |
| | | return result; |
| | | } |
| | | List<Long> roleIds = roleUserMapper.selectList(Wrappers.<SysRoleUser>lambdaQuery() |
| | | .eq(SysRoleUser::getUserId, userId)) |
| | | .stream().map(SysRoleUser::getRoleId).collect(Collectors.toList()); |
| | | if (roleIds.isEmpty()) { |
| | | return result; |
| | | } |
| | | Set<Long> roleMenuIds = new LinkedHashSet<>(roleMenuMapper.selectList(Wrappers.<SysRoleMenu>lambdaQuery() |
| | | .in(SysRoleMenu::getRoleId, roleIds)) |
| | | .stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList())); |
| | | Set<Long> tenantMenuIds = new LinkedHashSet<>(tenantMenuMapper.selectList(Wrappers.<SysTenantMenu>lambdaQuery() |
| | | .eq(SysTenantMenu::getTenantId, 1L)) |
| | | .stream().map(SysTenantMenu::getMenuId).collect(Collectors.toList())); |
| | | if (tenantMenuIds.isEmpty()) { |
| | | result.addAll(roleMenuIds); |
| | | } else { |
| | | for (Long menuId : roleMenuIds) { |
| | | if (tenantMenuIds.contains(menuId)) { |
| | | result.add(menuId); |
| | | } |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | public List<SysMenu> menuTreeForUser(String username) { |
| | | Set<Long> allowed = new LinkedHashSet<>(effectiveMenuIdsForUser(username)); |
| | | if (allowed.isEmpty() && !"admin".equals(username)) { |
| | | SysMenu dashboard = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getUrl, "/dashboard").last("LIMIT 1")); |
| | | if (dashboard != null) { |
| | | allowed.add(dashboard.getId()); |
| | | } |
| | | } |
| | | final Set<Long> allowedIds = allowed; |
| | | List<SysMenu> navigable = listMenus().stream() |
| | | .filter(menu -> menu.getMenuType() == null || menu.getMenuType() != 2) |
| | | .filter(menu -> allowedIds.contains(menu.getId())) |
| | | .collect(Collectors.toList()); |
| | | return buildTree(navigable); |
| | | } |
| | | |
| | | public List<String> permissionCodes(String username) { |
| | | Set<Long> menuIds = effectiveMenuIdsForUser(username); |
| | | return listMenus().stream() |
| | | .filter(menu -> menuIds.contains(menu.getId())) |
| | | .map(SysMenu::getPermissions) |
| | | .filter(StringUtils::hasText) |
| | | .distinct() |
| | | .sorted() |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | public Map<String, Object> menuButtons(Long menuId) { |
| | | SysMenu menu = menuMapper.selectById(menuId); |
| | | if (menu == null) { |
| | | throw new BusinessException("èåä¸åå¨"); |
| | | } |
| | | List<Map<String, Object>> available = new ArrayList<>(); |
| | | for (ButtonPermissionCatalog.ButtonDefinition definition : buttonPermissionCatalog.buttonsFor(menu.getUrl())) { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | row.put("code", definition.getCode()); |
| | | row.put("name", definition.getName()); |
| | | available.add(row); |
| | | } |
| | | List<String> selected = menuMapper.selectList(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getPid, menuId) |
| | | .eq(SysMenu::getMenuType, 2)) |
| | | .stream().map(SysMenu::getPermissions).filter(StringUtils::hasText).collect(Collectors.toList()); |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("menuId", String.valueOf(menuId)); |
| | | result.put("menuName", menu.getName()); |
| | | result.put("available", available); |
| | | result.put("selected", selected); |
| | | return result; |
| | | } |
| | | |
| | | @Transactional |
| | | public void syncMenuButtons(Long menuId, List<String> codes) { |
| | | SysMenu menu = menuMapper.selectById(menuId); |
| | | if (menu == null) { |
| | | throw new BusinessException("èåä¸åå¨"); |
| | | } |
| | | if (menu.getMenuType() == null || menu.getMenuType() != 1) { |
| | | throw new BusinessException("åªæèåç±»åæè½é
ç½®æé®"); |
| | | } |
| | | List<ButtonPermissionCatalog.ButtonDefinition> definitions = buttonPermissionCatalog.buttonsFor(menu.getUrl()); |
| | | Map<String, ButtonPermissionCatalog.ButtonDefinition> definitionMap = new LinkedHashMap<>(); |
| | | for (ButtonPermissionCatalog.ButtonDefinition definition : definitions) { |
| | | definitionMap.put(definition.getCode(), definition); |
| | | } |
| | | Set<String> wanted = new LinkedHashSet<>(); |
| | | if (codes != null) { |
| | | for (String code : codes) { |
| | | if (StringUtils.hasText(code) && definitionMap.containsKey(code)) { |
| | | wanted.add(code); |
| | | } |
| | | } |
| | | } |
| | | List<SysMenu> existing = menuMapper.selectList(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getPid, menuId).eq(SysMenu::getMenuType, 2)); |
| | | Map<String, SysMenu> existingByCode = new HashMap<>(); |
| | | for (SysMenu child : existing) { |
| | | existingByCode.put(child.getPermissions(), child); |
| | | if (!wanted.contains(child.getPermissions())) { |
| | | menuMapper.deleteById(child.getId()); |
| | | roleMenuMapper.delete(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getMenuId, child.getId())); |
| | | tenantMenuMapper.delete(Wrappers.<SysTenantMenu>lambdaQuery().eq(SysTenantMenu::getMenuId, child.getId())); |
| | | } |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | for (int index = 0; index < definitions.size(); index++) { |
| | | ButtonPermissionCatalog.ButtonDefinition definition = definitions.get(index); |
| | | if (!wanted.contains(definition.getCode())) { |
| | | continue; |
| | | } |
| | | SysMenu child = existingByCode.get(definition.getCode()); |
| | | if (child == null) { |
| | | child = new SysMenu(); |
| | | child.setPid(menuId); |
| | | child.setName(definition.getName()); |
| | | child.setPermissions(definition.getCode()); |
| | | child.setMenuType(2); |
| | | child.setSortNo((index + 1) * 10); |
| | | child.setVisible(1); |
| | | child.setCreateTime(now); |
| | | child.setUpdateTime(now); |
| | | menuMapper.insert(child); |
| | | } else { |
| | | child.setName(definition.getName()); |
| | | child.setSortNo((index + 1) * 10); |
| | | child.setUpdateTime(now); |
| | | menuMapper.updateById(child); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public Page<SysLoginLog> loginLogPage(int current, int size, String keyword, String status, String startDate, String endDate) { |
| | | Page<SysLoginLog> page = new Page<>(current <= 0 ? 1 : current, size <= 0 ? 20 : size); |
| | | LambdaQueryWrapper<SysLoginLog> wrapper = Wrappers.lambdaQuery(); |
| | | if (StringUtils.hasText(keyword)) { |
| | | String value = keyword.trim(); |
| | | wrapper.and(item -> item.like(SysLoginLog::getUserName, value).or().like(SysLoginLog::getNickName, value)); |
| | | } |
| | | if (StringUtils.hasText(status)) { |
| | | wrapper.eq(SysLoginLog::getStatus, status.trim()); |
| | | } |
| | | LocalDateTime start = parseStart(startDate); |
| | | LocalDateTime end = parseEnd(endDate); |
| | | if (start != null) { |
| | | wrapper.ge(SysLoginLog::getLoginTime, start); |
| | | } |
| | | if (end != null) { |
| | | wrapper.le(SysLoginLog::getLoginTime, end); |
| | | } |
| | | wrapper.orderByDesc(SysLoginLog::getLoginTime); |
| | | return loginLogMapper.selectPage(page, wrapper); |
| | | } |
| | | |
| | | public Page<SysOperationLog> operationLogPage(int current, int size, String keyword, String module, String operationType, String startDate, String endDate) { |
| | | Page<SysOperationLog> page = new Page<>(current <= 0 ? 1 : current, size <= 0 ? 20 : size); |
| | | LambdaQueryWrapper<SysOperationLog> wrapper = Wrappers.lambdaQuery(); |
| | | if (StringUtils.hasText(keyword)) { |
| | | String value = keyword.trim(); |
| | | wrapper.and(item -> item.like(SysOperationLog::getUserName, value) |
| | | .or().like(SysOperationLog::getNickName, value) |
| | | .or().like(SysOperationLog::getRequestUri, value)); |
| | | } |
| | | if (StringUtils.hasText(module)) { |
| | | wrapper.eq(SysOperationLog::getModule, module.trim()); |
| | | } |
| | | if (StringUtils.hasText(operationType)) { |
| | | wrapper.eq(SysOperationLog::getOperationType, operationType.trim()); |
| | | } |
| | | LocalDateTime start = parseStart(startDate); |
| | | LocalDateTime end = parseEnd(endDate); |
| | | if (start != null) { |
| | | wrapper.ge(SysOperationLog::getCreateTime, start); |
| | | } |
| | | if (end != null) { |
| | | wrapper.le(SysOperationLog::getCreateTime, end); |
| | | } |
| | | wrapper.orderByDesc(SysOperationLog::getCreateTime); |
| | | return operationLogMapper.selectPage(page, wrapper); |
| | | } |
| | | |
| | | public Long resolveUserId(String username) { |
| | | if (!StringUtils.hasText(username)) { |
| | | return null; |
| | | } |
| | | List<Long> ids = jdbcTemplate.query( |
| | | "SELECT USER_ID FROM CSICZB_ZB.SYS_USER WHERE USER_NAME = ? AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL)", |
| | | new Object[]{username}, (rs, rowNum) -> rs.getLong(1)); |
| | | return ids.isEmpty() ? null : ids.get(0); |
| | | } |
| | | |
| | | private List<Long> expandWithAncestors(List<Long> ids) { |
| | | Set<Long> result = new LinkedHashSet<>(); |
| | | if (ids == null) { |
| | | return new ArrayList<>(result); |
| | | } |
| | | for (Long id : ids) { |
| | | Long cursor = id; |
| | | int guard = 0; |
| | | while (cursor != null && cursor != 0L && guard++ < 32) { |
| | | if (!result.add(cursor)) { |
| | | break; |
| | | } |
| | | SysMenu menu = menuMapper.selectById(cursor); |
| | | if (menu == null) { |
| | | break; |
| | | } |
| | | cursor = menu.getPid(); |
| | | } |
| | | } |
| | | return new ArrayList<>(result); |
| | | } |
| | | |
| | | private LocalDateTime parseStart(String value) { |
| | | if (!StringUtils.hasText(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return LocalDate.parse(value.trim()).atStartOfDay(); |
| | | } catch (Exception ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private LocalDateTime parseEnd(String value) { |
| | | if (!StringUtils.hasText(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return LocalDate.parse(value.trim()).atTime(23, 59, 59); |
| | | } catch (Exception ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | private Map<String, Object> mapProduct(ProductNode node) { |
| | | return mapProduct(node, Collections.emptySet()); |
| | | } |
| | | |
| | | private Map<String, Object> mapProduct(ProductNode node, Set<Long> branchIds) { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | row.put("id", String.valueOf(node.getId())); |
| | | row.put("parentId", node.getParentId() == null ? null : String.valueOf(node.getParentId())); |
| | | row.put("nodeLevel", node.getNodeLevel()); |
| | | row.put("nodeType", node.getNodeType()); |
| | | row.put("layer", node.getLayer()); |
| | | row.put("connectGroup", node.getConnectGroup()); |
| | | row.put("name", node.getName()); |
| | | row.put("code", node.getCode()); |
| | | row.put("baseEquipmentId", node.getBaseEquipmentId() == null ? null : String.valueOf(node.getBaseEquipmentId())); |
| | | row.put("project", node.getProject()); |
| | | row.put("sortNo", node.getSortNo()); |
| | | row.put("remark", node.getRemark()); |
| | | row.put("isLeaf", !branchIds.contains(node.getId())); |
| | | row.put("children", new ArrayList<>()); |
| | | return row; |
| | | } |
| | | |
| | | private int productNodeLevel(ProductNode node) { |
| | | Integer level = node.getNodeLevel(); |
| | | return level == null ? Integer.MAX_VALUE : level; |
| | | } |
| | | |
| | | private String resolveProductLayer(ProductNode node) { |
| | | String layer = ProductNode.layerForType(node.getNodeType()); |
| | | if (!StringUtils.hasText(layer)) { |
| | | layer = ProductNode.layerForLevel(node.getNodeLevel()); |
| | | } |
| | | if (!StringUtils.hasText(layer)) { |
| | | layer = StringUtils.hasText(node.getLayer()) ? node.getLayer().trim().toLowerCase() : null; |
| | | } |
| | | if (!StringUtils.hasText(layer)) { |
| | | throw new BusinessException("èç¹å±çº§ä¸è½ä¸ºç©º"); |
| | | } |
| | | return layer; |
| | | } |
| | | |
| | | private String buildConnectGroup(Long parentId) { |
| | | if (parentId == null || parentId == 0L) { |
| | | return "0"; |
| | | } |
| | | ProductNode parent = findProductNode(parentId); |
| | | if (parent == null) { |
| | | throw new BusinessException("ç¶èç¹ä¸åå¨"); |
| | | } |
| | | String prefix = StringUtils.hasText(parent.getConnectGroup()) ? parent.getConnectGroup() : "0"; |
| | | return prefix + "," + parent.getId(); |
| | | } |
| | | |
| | | private ProductNode findProductNode(Long id) { |
| | | if (id == null) { |
| | | return null; |
| | | } |
| | | List<ProductNode> nodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .eq(ProductNode::getId, id) |
| | | .orderByAsc(ProductNode::getRecordId) |
| | | .last("LIMIT 1")); |
| | | return nodes.isEmpty() ? null : nodes.get(0); |
| | | } |
| | | |
| | | private Map<String, Object> mapBase(BaseEquipment node) { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | row.put("id", String.valueOf(node.getId())); |
| | |
| | | fault-code: |
| | | prefix: "åºå®åç¼-" |
| | | server: |
| | | port: 8091 |
| | | servlet: |
| | |
| | | NVL(SUM(CASE WHEN IS_MONITOR_ITEM = 1 THEN 1 ELSE 0 END), 0) AS MONITOR_ITEM, |
| | | NVL(SUM(CASE WHEN TASK_STATUS = 'CANCELLED' THEN 1 ELSE 0 END), 0) AS CANCELLED |
| | | FROM LX.T_FAULT |
| | | WHERE DEL_FLAG = 0 |
| | | ) f |
| | | CROSS JOIN ( |
| | | SELECT NVL(SUM(CASE WHEN COMPLETION_STATUS IS NULL OR COMPLETION_STATUS <> 'COMPLETED' THEN 1 ELSE 0 END), 0) AS UNFINISHED, |
| | | NVL(SUM(CASE WHEN COMPLETION_STATUS = 'COMPLETED' THEN 1 ELSE 0 END), 0) AS COMPLETED, |
| | | NVL(SUM(CASE WHEN COMPLETION_STATUS = 'WAIT_TEST' THEN 1 ELSE 0 END), 0) AS WAIT_TEST |
| | | FROM LX.T_WORK_ORDER |
| | | WHERE DEL_FLAG = 0 |
| | | ) w |
| | | </select> |
| | | |
| | |
| | | OR (NVL(f.EQUIPMENT_IDS, '') = '' AND f.EQUIPMENT_ID = p.ID) |
| | | ) |
| | | JOIN LX.T_BASE_EQUIPMENT b ON b.ID = p.BASE_EQUIPMENT_ID |
| | | WHERE f.DEL_FLAG = 0 AND p.DEL_FLAG = 0 AND b.DEL_FLAG = 0 |
| | | ), |
| | | splitter AS ( |
| | | SELECT LEVEL AS N |
| | |
| | | 'ã' |
| | | )), 0) + 1 |
| | | FROM LX.T_FAULT |
| | | WHERE DEL_FLAG = 0 |
| | | ) |
| | | ), |
| | | fallback_names AS ( |
| | |
| | | REPLACE(REPLACE(REPLACE(NVL(f.EQUIPMENT_NAME, ''), 'ï¼', 'ã'), ',', 'ã'), CHR(10), 'ã'), |
| | | 'ã' |
| | | ) + 1 |
| | | WHERE f.DEL_FLAG = 0 |
| | | ), |
| | | fallback_link AS ( |
| | | SELECT DISTINCT fn.FAULT_ID, |
| | |
| | | ON p5.NODE_LEVEL = 5 |
| | | AND p5.NAME = fn.EQUIPMENT_NAME |
| | | AND p5.BASE_EQUIPMENT_ID IS NOT NULL |
| | | JOIN LX.T_PRODUCT_TREE p4 ON p4.ID = p5.PARENT_ID |
| | | JOIN LX.T_PRODUCT_TREE p3 ON p3.ID = p4.PARENT_ID |
| | | AND p5.DEL_FLAG = 0 |
| | | JOIN LX.T_PRODUCT_TREE p4 ON p4.ID = p5.PARENT_ID AND p4.DEL_FLAG = 0 |
| | | JOIN LX.T_PRODUCT_TREE p3 ON p3.ID = p4.PARENT_ID AND p3.DEL_FLAG = 0 |
| | | JOIN LX.T_PRODUCT_TREE x |
| | | ON x.ID = p3.PARENT_ID |
| | | AND x.NODE_LEVEL = 2 |
| | | AND x.NAME = f.X_NO |
| | | JOIN LX.T_BASE_EQUIPMENT b ON b.ID = p5.BASE_EQUIPMENT_ID |
| | | AND x.DEL_FLAG = 0 |
| | | JOIN LX.T_BASE_EQUIPMENT b ON b.ID = p5.BASE_EQUIPMENT_ID AND b.DEL_FLAG = 0 |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 |
| | | FROM explicit_link el |
| | |
| | | <select id="selectByTeam" resultMap="teamResultMap"> |
| | | WITH fault_agg AS ( |
| | | SELECT CASE |
| | | WHEN TRIM(TEAM_MAJOR) IS NULL OR TRIM(TEAM_MAJOR) = '' THEN 'æªå¡«åTéä¸ä¸' |
| | | WHEN TRIM(TEAM_MAJOR) IS NULL OR TRIM(TEAM_MAJOR) = '' THEN 'æªå¡«årk_Téä¸ä¸' |
| | | ELSE TRIM(TEAM_MAJOR) |
| | | END AS NAME, |
| | | COUNT(*) AS FAULT_COUNT |
| | | FROM LX.T_FAULT |
| | | WHERE DEL_FLAG = 0 |
| | | GROUP BY CASE |
| | | WHEN TRIM(TEAM_MAJOR) IS NULL OR TRIM(TEAM_MAJOR) = '' THEN 'æªå¡«åTéä¸ä¸' |
| | | WHEN TRIM(TEAM_MAJOR) IS NULL OR TRIM(TEAM_MAJOR) = '' THEN 'æªå¡«årk_Téä¸ä¸' |
| | | ELSE TRIM(TEAM_MAJOR) |
| | | END |
| | | ), |
| | | parts_agg AS ( |
| | | SELECT CASE |
| | | WHEN f.TEAM_MAJOR IS NULL OR TRIM(f.TEAM_MAJOR) = '' THEN 'æªå¡«åTéä¸ä¸' |
| | | WHEN f.TEAM_MAJOR IS NULL OR TRIM(f.TEAM_MAJOR) = '' THEN 'æªå¡«årk_Téä¸ä¸' |
| | | ELSE TRIM(f.TEAM_MAJOR) |
| | | END AS NAME, |
| | | NVL(SUM(NVL(ci.CONSUMABLE_COUNT, 0)), 0) AS PARTS_COUNT |
| | | FROM LX.T_COMPLETION_INFO ci |
| | | JOIN LX.T_WORK_ORDER w ON w.ID = ci.WORK_ORDER_ID |
| | | LEFT JOIN LX.T_FAULT f ON f.ID = w.FAULT_ID |
| | | JOIN LX.T_WORK_ORDER w ON w.ID = ci.WORK_ORDER_ID AND w.DEL_FLAG = 0 |
| | | LEFT JOIN LX.T_FAULT f ON f.ID = w.FAULT_ID AND f.DEL_FLAG = 0 |
| | | WHERE ci.DEL_FLAG = 0 |
| | | GROUP BY CASE |
| | | WHEN f.TEAM_MAJOR IS NULL OR TRIM(f.TEAM_MAJOR) = '' THEN 'æªå¡«åTéä¸ä¸' |
| | | WHEN f.TEAM_MAJOR IS NULL OR TRIM(f.TEAM_MAJOR) = '' THEN 'æªå¡«årk_Téä¸ä¸' |
| | | ELSE TRIM(f.TEAM_MAJOR) |
| | | END |
| | | ), |
| | |
| | | NVL(SUM(CASE WHEN COMPLETION_STATUS = 'COMPLETED' THEN 1 ELSE 0 END), 0) AS COMPLETED_COUNT, |
| | | NVL(ROUND(AVG(SCORE), 1), 0) AS AVERAGE_SCORE |
| | | FROM LX.T_WORK_ORDER |
| | | WHERE DEL_FLAG = 0 |
| | | GROUP BY CONTRACTOR_NAME |
| | | ORDER BY TOTAL_COUNT DESC, CONTRACTOR_NAME ASC |
| | | </select> |
| | |
| | | SELECT EXTRACT(YEAR FROM REPORT_DATE) AS REPORT_YEAR, |
| | | COUNT(*) AS FAULT_COUNT |
| | | FROM LX.T_FAULT |
| | | WHERE REPORT_DATE IS NOT NULL |
| | | WHERE DEL_FLAG = 0 AND REPORT_DATE IS NOT NULL |
| | | GROUP BY EXTRACT(YEAR FROM REPORT_DATE) |
| | | ), |
| | | parts_agg AS ( |
| | | SELECT EXTRACT(YEAR FROM f.REPORT_DATE) AS REPORT_YEAR, |
| | | NVL(SUM(NVL(ci.CONSUMABLE_COUNT, 0)), 0) AS PARTS_COUNT |
| | | FROM LX.T_COMPLETION_INFO ci |
| | | JOIN LX.T_WORK_ORDER w ON w.ID = ci.WORK_ORDER_ID |
| | | JOIN LX.T_FAULT f ON f.ID = w.FAULT_ID |
| | | WHERE f.REPORT_DATE IS NOT NULL |
| | | JOIN LX.T_WORK_ORDER w ON w.ID = ci.WORK_ORDER_ID AND w.DEL_FLAG = 0 |
| | | JOIN LX.T_FAULT f ON f.ID = w.FAULT_ID AND f.DEL_FLAG = 0 |
| | | WHERE ci.DEL_FLAG = 0 AND f.REPORT_DATE IS NOT NULL |
| | | GROUP BY EXTRACT(YEAR FROM f.REPORT_DATE) |
| | | ), |
| | | report_years AS ( |
| | |
| | | { |
| | | "sensitive_words": [ |
| | | { |
| | | "codename": "Xå·", |
| | | "codename": "rk_Xå·", |
| | | "real_name": "å¿å·", |
| | | "scope": "ui", |
| | | "note": "Development mode displays the codename. Replace real_name only in the deployed production dictionary." |
| | | }, |
| | | { |
| | | "codename": "JC", |
| | | "codename": "rk_JC", |
| | | "real_name": "JC", |
| | | "scope": "ui", |
| | | "note": "JCä¿¡æ¯ = JC information." |
| | | }, |
| | | { |
| | | "codename": "JCC", |
| | | "codename": "rk_JCC", |
| | | "real_name": "JCC", |
| | | "scope": "ui", |
| | | "note": "JCC assistant, director, and acceptance roles." |
| | | }, |
| | | { |
| | | "codename": "CMB", |
| | | "codename": "rk_CMB", |
| | | "real_name": "CMB", |
| | | "scope": "ui", |
| | | "note": "CMB leader approval role." |
| | | }, |
| | | { |
| | | "codename": "T", |
| | | "real_name": "T", |
| | | "scope": "ui", |
| | | "note": "Word root. Téä¸ä¸, Tå
æ½å·¥, and Tå all use the codename in development." |
| | | }, |
| | | { |
| | | "codename": "TD", |
| | | "codename": "rk_TD", |
| | | "real_name": "TD", |
| | | "scope": "ui", |
| | | "note": "Home page statistic column." |
| | | }, |
| | | { |
| | | "codename": "XCB", |
| | | "codename": "rk_T", |
| | | "real_name": "T", |
| | | "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": "H", |
| | | "codename": "rk_H", |
| | | "real_name": "H", |
| | | "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." |
| | | "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" |
| | | } |
| | | } |
| | |
| | | api.interceptors.request.use(config => { |
| | | const token = sessionStorage.getItem('token') |
| | | if (token) config.headers.Authorization = 'Bearer ' + token |
| | | const username = sessionStorage.getItem('username') |
| | | if (username) config.headers['X-Username'] = username |
| | | return config |
| | | }) |
| | | |
| | |
| | | <template> |
| | | <el-submenu v-if="item.children && item.children.length" :index="String(item.id)"> |
| | | <el-submenu v-if="item.menuType !== 2 && item.children && item.children.length" :index="String(item.id)"> |
| | | <template slot="title"> |
| | | <i :class="item.icon || 'el-icon-menu'"></i> |
| | | <span slot="title">{{ item.name }}</span> |
| | | </template> |
| | | <menu-node v-for="child in item.children" :key="child.id" :item="child"></menu-node> |
| | | </el-submenu> |
| | | <el-menu-item v-else-if="item.url" :index="item.url"> |
| | | <el-menu-item v-else-if="item.menuType !== 2 && item.url" :index="item.url"> |
| | | <i :class="item.icon || 'el-icon-tickets'"></i> |
| | | <span slot="title">{{ item.name }}</span> |
| | | </el-menu-item> |
| | |
| | | ref="tree" |
| | | :data="tree" |
| | | node-key="id" |
| | | default-expand-all |
| | | v-loading="loading" |
| | | lazy |
| | | :load="loadNode" |
| | | show-checkbox |
| | | check-strictly |
| | | :filter-node-method="filterNode" |
| | |
| | | selectedIds: { type: Array, default: () => [] } |
| | | }, |
| | | data() { |
| | | return { localVisible: false, keyword: '', tree: [], checkedCount: 0, selectedDevices: [] } |
| | | return { localVisible: false, keyword: '', tree: [], checkedCount: 0, selectedDevices: [], selectedMap: {}, loading: false } |
| | | }, |
| | | computed: { |
| | | treeProps() { |
| | |
| | | methods: { |
| | | load() { |
| | | const lockedXNo = String(this.xNo || '').trim() |
| | | api.get('/system/tree', { params: { type: 'product' } }).then(res => { |
| | | const source = res.data || [] |
| | | this.tree = lockedXNo ? this.filterTreeByXNo(source, lockedXNo) : source |
| | | this.loading = true |
| | | const ids = (this.selectedIds || []).map(String).filter(Boolean) |
| | | const treeRequest = lockedXNo |
| | | ? api.get('/system/tree/product/side-roots', { params: { xNo: lockedXNo } }) |
| | | : api.get('/system/tree/product/roots') |
| | | const selectedRequest = ids.length |
| | | ? api.get('/system/tree/product/selected', { params: { ids: ids.join(',') } }) |
| | | : Promise.resolve({ data: [] }) |
| | | Promise.all([treeRequest, selectedRequest]).then(([treeRes, selectedRes]) => { |
| | | this.tree = treeRes.data || [] |
| | | this.selectedMap = (selectedRes.data || []).reduce((map, node) => { |
| | | map[String(node.id)] = node |
| | | return map |
| | | }, {}) |
| | | this.keyword = '' |
| | | this.$nextTick(this.restoreChecked) |
| | | }) |
| | | this.$nextTick(() => { this.restoreChecked(); this.syncSelected() }) |
| | | }).finally(() => { this.loading = false }) |
| | | }, |
| | | filterTreeByXNo(nodes, xNo) { |
| | | const result = [] |
| | | ;(nodes || []).forEach(node => { |
| | | if (Number(node.nodeLevel) === 2) { |
| | | if (String(node.name || '').trim() === xNo) result.push(node) |
| | | loadNode(node, resolve) { |
| | | if (!node || !node.data || !node.data.id) { |
| | | resolve([]) |
| | | return |
| | | } |
| | | const children = this.filterTreeByXNo(node.children || [], xNo) |
| | | if (children.length || node.nodeType === 'DEVICE') { |
| | | result.push(children.length ? { ...node, children } : node) |
| | | } |
| | | api.get('/system/tree/product/children', { params: { parentId: node.data.id } }) |
| | | .then(res => { |
| | | resolve(res.data || []) |
| | | this.$nextTick(this.restoreChecked) |
| | | }) |
| | | return result |
| | | .catch(() => resolve([])) |
| | | }, |
| | | restoreChecked() { |
| | | if (!this.$refs.tree) return |
| | | const ids = (this.selectedIds || []).map(String).filter(Boolean) |
| | | const ids = Object.keys(this.selectedMap).filter(id => Boolean(this.$refs.tree.getNode(id))) |
| | | this.$refs.tree.setCheckedKeys(ids) |
| | | this.syncSelected() |
| | | }, |
| | | handleCheck(node) { |
| | | const tree = this.$refs.tree |
| | | const treeNode = tree && tree.getNode(node.id) |
| | | this.syncSelected() |
| | | if (treeNode && treeNode.checked) { |
| | | const selected = this.deviceNodes().map(item => this.decorateNode(item)) |
| | | const xNos = Array.from(new Set(selected.map(item => item.xNo).filter(Boolean))) |
| | | const xNos = Array.from(new Set(this.selectedDevices.map(item => item.xNo).filter(Boolean))) |
| | | if (xNos.length > 1) { |
| | | tree.setChecked(node.id, false, true) |
| | | this.$message.warning('å䏿
éåªè½éæ©åä¸ä¸ªXå·ä¸ç设å¤') |
| | | } |
| | | } |
| | | this.syncSelected() |
| | | this.$message.warning('å䏿
éåªè½éæ©åä¸ä¸ªrk_Xå·ä¸ç设å¤') |
| | | } |
| | | } |
| | | }, |
| | | syncSelected() { |
| | | const selected = this.deviceNodes().map(node => this.decorateNode(node)) |
| | | this.selectedDevices = selected |
| | | this.checkedCount = selected.length |
| | | const tree = this.$refs.tree |
| | | if (!tree) return |
| | | const selectedMap = { ...this.selectedMap } |
| | | const checked = tree.getCheckedNodes().filter(node => node.nodeType === 'DEVICE') |
| | | const checkedIds = new Set(checked.map(node => String(node.id))) |
| | | Object.keys(selectedMap).forEach(id => { |
| | | if (tree.getNode(id) && !checkedIds.has(id)) delete selectedMap[id] |
| | | }) |
| | | checked.forEach(node => { selectedMap[String(node.id)] = this.decorateNode(node) }) |
| | | this.selectedMap = selectedMap |
| | | this.selectedDevices = Object.values(selectedMap) |
| | | this.checkedCount = this.selectedDevices.length |
| | | }, |
| | | decorateNode(node) { |
| | | if (!this.$refs.tree) return { ...node } |
| | | const pathNodes = [] |
| | | let current = this.$refs.tree.getNode(node.id) |
| | | while (current && current.data) { |
| | |
| | | return { ...node, pathNames: pathNodes.map(item => item.name), parentPathNames, xNo: xNode ? xNode.name : '' } |
| | | }, |
| | | removeSelected(device) { |
| | | if (!this.$refs.tree) return |
| | | this.$refs.tree.setChecked(device.id, false, true) |
| | | const selectedMap = { ...this.selectedMap } |
| | | delete selectedMap[String(device.id)] |
| | | this.selectedMap = selectedMap |
| | | if (this.$refs.tree && this.$refs.tree.getNode(device.id)) this.$refs.tree.setChecked(device.id, false, true) |
| | | this.syncSelected() |
| | | }, |
| | | deviceNodes() { |
| | |
| | | return (data.name || '').includes(value) || (data.code || '').includes(value) |
| | | }, |
| | | confirm() { |
| | | const nodes = this.deviceNodes() |
| | | const nodes = this.selectedDevices |
| | | if (!nodes.length) return this.$message.warning('è¯·éæ©è®¾å¤') |
| | | const selected = nodes.map(node => this.decorateNode(node)) |
| | | const selected = nodes |
| | | const xNos = Array.from(new Set(selected.map(item => item.xNo).filter(Boolean))) |
| | | if (xNos.length > 1) { |
| | | return this.$message.warning('å䏿
éåªè½éæ©åä¸ä¸ªXå·ä¸ç设å¤') |
| | | return this.$message.warning('å䏿
éåªè½éæ©åä¸ä¸ªrk_Xå·ä¸ç设å¤') |
| | | } |
| | | this.$emit('select', selected) |
| | | this.localVisible = false |
| | |
| | | <template> |
| | | <svg class="repair-logo" viewBox="0 0 30 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> |
| | | <rect class="screwdriver-handle" x="5" y="2" width="3.2" height="6" rx="1.2"/> |
| | | <path d="M5.7 4.2h1.8"/> |
| | | <path d="M5.7 6.2h1.8"/> |
| | | <path d="M6.6 8v7"/> |
| | | <path d="M5.6 15h2"/> |
| | | <path d="M5.6 17.5h2"/> |
| | | <g transform="translate(5 -2) scale(.82)"> |
| | | <path vector-effect="non-scaling-stroke" d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/> |
| | | </g> |
| | | </svg> |
| | | <img class="repair-logo" :src="logoSrc" alt="临æ¢ä¿®ç®¡çç³»ç»"> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'RepairLogo' |
| | | name: 'RepairLogo', |
| | | computed: { |
| | | logoSrc() { |
| | | return (process.env.BASE_URL || '/') + 'logo.png' |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .repair-logo { |
| | | display: block; |
| | | width: 1.30em; |
| | | height: 1.03em; |
| | | overflow: visible; |
| | | } |
| | | .screwdriver-handle { |
| | | fill: currentColor; |
| | | fill-opacity: .16; |
| | | width: 1.2em; |
| | | height: 1.2em; |
| | | object-fit: contain; |
| | | } |
| | | </style> |
| | |
| | | } |
| | | |
| | | const REPORT_TITLES = { |
| | | overview: 'è£
夿
éæ»ä½åæ', |
| | | overview: 'è®¾å¤æ
éæ»ä½åæ', |
| | | 'top-equipment': 'æ
éé«å设å¤Top', |
| | | 'by-team': 'æTç»è®¡', |
| | | 'by-team': 'rk_æTç»è®¡', |
| | | contractors: 'åå®¶ç»´ä¿®æ
åµ', |
| | | trend: '年度è¶å¿' |
| | | } |
| | |
| | | overflow-wrap: anywhere; |
| | | word-break: break-word; |
| | | } |
| | | .mdi-tabbar { display: flex; flex: 0 0 40px; align-items: stretch; height: 40px; padding-top: 5px; border-bottom: 1px solid #cfdae5; background: #e3eaf1; } |
| | | .mdi-tabs-scroll { flex: 1; min-width: 0; overflow-x: auto; overflow-y: hidden; } |
| | | .mdi-tabs { display: inline-flex; align-items: flex-end; min-width: 100%; height: 100%; padding: 0 4px; } |
| | | .mdi-tabbar { display: flex; flex: 0 0 50px; align-items: stretch; height: 50px; padding-top: 5px; border-bottom: 1px solid #cfdae5; background: #e3eaf1; } |
| | | .mdi-tabs-scroll { flex: 1; min-width: 0; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; scrollbar-color: #9fb1c1 transparent; } |
| | | .mdi-tabs-scroll::-webkit-scrollbar { height: 6px; } |
| | | .mdi-tabs-scroll::-webkit-scrollbar-track { background: transparent; } |
| | | .mdi-tabs-scroll::-webkit-scrollbar-thumb { border-radius: 6px; background: #9fb1c1; } |
| | | .mdi-tabs { display: inline-flex; align-items: flex-end; min-width: 100%; height: 35px; padding: 0 4px; } |
| | | .mdi-tab { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 6px; max-width: 220px; height: 34px; margin-right: 2px; padding: 0 9px; border: 1px solid transparent; border-bottom: 0; border-radius: 4px 4px 0 0; color: #5c6f82; background: rgba(255, 255, 255, .42); cursor: pointer; font-size: 13px; user-select: none; } |
| | | .mdi-tab:hover { color: #245275; background: rgba(255, 255, 255, .72); } |
| | | .mdi-tab.is-active { height: 35px; color: #1f3348; background: #eef2f6; border-color: #cfdae5; box-shadow: inset 0 2px 0 #2c7fb8; } |
| | |
| | | |
| | | Vue.prototype.$appName = '临æ¢ä¿®ç®¡çç³»ç»' |
| | | |
| | | Vue.prototype.$hasPermission = function(code) { |
| | | const raw = sessionStorage.getItem('permissions') |
| | | if (raw === null) return true |
| | | try { |
| | | const list = JSON.parse(raw) |
| | | if (!Array.isArray(list)) return true |
| | | return list.indexOf(code) >= 0 |
| | | } catch (e) { |
| | | return true |
| | | } |
| | | } |
| | | |
| | | Vue.directive('permission', { |
| | | inserted(el, binding) { |
| | | if (binding.value && !Vue.prototype.$hasPermission(binding.value) && el.parentNode) { |
| | | el.parentNode.removeChild(el) |
| | | } |
| | | } |
| | | }) |
| | | |
| | | new Vue({ |
| | | router, |
| | | render: h => h(App) |
| | |
| | | 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: 'XCBå©çå®¡æ ¸', cacheName: 'XcbReview' } }, |
| | | { path: 'fault/approval/cmb', component: () => import('@/views/fault/SimpleApproval.vue'), props: { stage: 'CMB' }, meta: { title: 'CMBé¢å¯¼å®¡æ¹', stage: 'CMB', cacheName: 'SimpleApproval' } }, |
| | | { path: 'fault/approval/jcc-assistant', component: () => import('@/views/fault/SimpleApproval.vue'), props: { stage: 'JCC_ASSISTANT' }, meta: { title: 'JCCå©ç审æ¹', stage: 'JCC_ASSISTANT', cacheName: 'SimpleApproval' } }, |
| | | { path: 'fault/approval/jcc-director', component: () => import('@/views/fault/SimpleApproval.vue'), props: { stage: 'JCC_DIRECTOR' }, meta: { title: 'JCCå¤é¿å®¡æ¹', stage: 'JCC_DIRECTOR', cacheName: 'SimpleApproval' } }, |
| | | { path: 'fault/dispatch', component: () => import('@/views/fault/DispatchPage.vue'), meta: { title: 'XCBå©çæ´¾å', cacheName: 'DispatchPage' } }, |
| | | { 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/completion', component: () => import('@/views/completion/CompletionList.vue'), meta: { title: 'å®å·¥æ
åµå¤ç', cacheName: 'CompletionList' } }, |
| | | { path: 'completion/report', component: () => import('@/views/completion/CompletionReport.vue'), props: route => ({ workOrderId: route.query.workOrderId || null }), meta: { title: 'å®å·¥ä¿¡æ¯ä¸æ¥', cacheName: 'CompletionReport' } }, |
| | | { path: 'acceptance/application', component: () => import('@/views/acceptance/AcceptanceList.vue'), props: { stage: 'APPLICATION' }, meta: { title: 'éªæ¶ç³è¯·', stage: 'APPLICATION', cacheName: 'AcceptanceList' } }, |
| | |
| | | { path: 'acceptance/rating', component: () => import('@/views/acceptance/AcceptanceList.vue'), props: { stage: 'RATING' }, meta: { title: 'ä¿®åè´¨éè¯çº§', stage: 'RATING', cacheName: 'AcceptanceList' } }, |
| | | { path: 'reports/:type', component: () => import('@/views/report/ReportPage.vue'), props: route => ({ type: route.params.type }), meta: { title: 'ç»è®¡æ¥è¡¨', cacheName: 'ReportPage' } }, |
| | | { path: 'system/contractors', component: () => import('@/views/system/ContractorManage.vue'), meta: { title: 'æ¿ä¿®åä½ç®¡ç', cacheName: 'ContractorManage' } }, |
| | | { path: 'system/organization', component: () => import('@/views/system/OrganizationManage.vue'), meta: { title: 'ç»ç»æºæ', cacheName: 'OrganizationManage' } }, |
| | | { path: 'system/product-tree', component: () => import('@/views/system/TreeManage.vue'), props: { treeType: 'product' }, meta: { title: '产åç»ææ 管ç', treeType: 'product', cacheName: 'TreeManage' } }, |
| | | { path: 'system/base-equipment', component: () => import('@/views/system/TreeManage.vue'), props: { treeType: 'base' }, meta: { title: '设å¤åºç¡åºç®¡ç', treeType: 'base', cacheName: 'TreeManage' } }, |
| | | { path: 'system/engineering-majors', component: () => import('@/views/system/EngineeringMajorManage.vue'), meta: { title: 'å·¥ç¨ä¸ä¸é
ç½®', cacheName: 'EngineeringMajorManage' } }, |
| | | { path: 'system/team-majors', component: () => import('@/views/system/TeamMajorManage.vue'), meta: { title: 'Téä¸ä¸é
ç½®', cacheName: 'TeamMajorManage' } }, |
| | | { path: 'system/team-majors', component: () => import('@/views/system/TeamMajorManage.vue'), meta: { title: 'rk_Téä¸ä¸é
ç½®', cacheName: 'TeamMajorManage' } }, |
| | | { path: 'system/users', component: () => import('@/views/system/UserManage.vue'), meta: { title: 'ç¨æ·ç®¡ç', cacheName: 'UserManage' } }, |
| | | { path: 'system/roles', component: () => import('@/views/system/RoleManage.vue'), meta: { title: 'è§è²ç®¡ç', cacheName: 'RoleManage' } }, |
| | | { path: 'system/login-logs', component: () => import('@/views/system/LoginLogManage.vue'), meta: { title: 'ç»å½æ¥å¿', cacheName: 'LoginLogManage' } }, |
| | | { path: 'system/operation-logs', component: () => import('@/views/system/OperationLogManage.vue'), meta: { title: 'æä½æ¥å¿', cacheName: 'OperationLogManage' } }, |
| | | { path: 'system/menus', component: () => import('@/views/system/MenuManage.vue'), meta: { title: 'èå管ç', cacheName: 'MenuManage' } }, |
| | | { path: 'system/menu-permissions', component: () => import('@/views/system/MenuPermission.vue'), meta: { title: 'èåæé', cacheName: 'MenuPermission' } }, |
| | | { path: 'system/three-member', component: () => import('@/views/system/ThreeMember.vue'), meta: { title: 'ä¸å管ç', cacheName: 'ThreeMember' } } |
| | |
| | | export const taskStatuses = { |
| | | UNREPORTED: { text: 'æªä¸æ¥', type: 'info' }, |
| | | REPORTED_XCB: { text: '已䏿¥XCB', type: 'warning' }, |
| | | REPORTED_XCB: { text: '已䏿¥rk_XCB', type: 'warning' }, |
| | | RETURNED: { text: 'ä¸çº§æå', type: 'danger' }, |
| | | XCB_REVIEWED: { text: 'XCBå©çå®¡æ ¸å®æ', type: 'primary' }, |
| | | CMB_APPROVED: { text: 'CMBé¢å¯¼å®¡æ¹å®æ', type: 'success' }, |
| | | JCC_ASSISTANT_APPROVED: { text: 'JCCå©ç审æ¹å®æ', type: 'success' }, |
| | | JCC_DIRECTOR_APPROVED: { text: 'JCCå¤é¿å·²é
', type: 'success' }, |
| | | 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' }, |
| | | DISPATCHED: { text: '已派å', type: 'primary' }, |
| | | CANCELLED: { text: 'å·²é项', type: 'info' } |
| | | } |
| | |
| | | }) |
| | | } |
| | | |
| | | function expandPrefixedAliases(items) { |
| | | const expanded = [] |
| | | const seen = {} |
| | | items.forEach(function (item) { |
| | | const names = [item.codename] |
| | | if (item.codename.indexOf('rk_') === 0 && item.codename.length > 3) { |
| | | names.push(item.codename.substring(3)) |
| | | } |
| | | names.forEach(function (codename) { |
| | | if (!codename || seen[codename]) { |
| | | return |
| | | } |
| | | seen[codename] = true |
| | | expanded.push({ codename: codename, real_name: item.real_name }) |
| | | }) |
| | | }) |
| | | return expanded |
| | | } |
| | | export async function installSensitiveWords() { |
| | | try { |
| | | const baseUrl = process.env.BASE_URL || '/' |
| | |
| | | .filter(function (item) { |
| | | return item && item.codename && isConfigured(item.real_name, item.codename) |
| | | }) |
| | | .sort(function (left, right) { |
| | | |
| | | replacements = expandPrefixedAliases(replacements).sort(function (left, right) { |
| | | return right.codename.length - left.codename.length |
| | | }) |
| | | |
| | |
| | | <div class="dashboard-grid"> |
| | | <section class="dashboard-panel chart-panel"> |
| | | <div class="panel-head"> |
| | | <h3>è£
夿
éæ»ä½åæ</h3> |
| | | <h3>è®¾å¤æ
éæ»ä½åæ</h3> |
| | | <span>䏿¹æ±ç¶å¾ / 䏿¹ç»è®¡</span> |
| | | </div> |
| | | <div ref="overviewChart" class="chart-box"></div> |
| | |
| | | <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>XCBå©çå®¡æ ¸</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/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> |
| | |
| | | |
| | | <section class="dashboard-panel"> |
| | | <div class="panel-head"> |
| | | <h3>æXå·ç»è®¡</h3> |
| | | <span>æXå·æ±æ»å½åä»»å¡åå®å·¥ç¶æ</span> |
| | | <h3>ærk_Xå·ç»è®¡</h3> |
| | | <span>ærk_Xå·æ±æ»å½åä»»å¡åå®å·¥ç¶æ</span> |
| | | </div> |
| | | <el-table :data="xStats" border size="small"> |
| | | <el-table-column prop="xNo" label="Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="xNo" label="rk_Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="currentStatus" label="å½åç¶æ" width="110"> |
| | | <template slot-scope="{ row }"><el-tag size="mini" type="info">{{ row.currentStatus }}</el-tag></template> |
| | | </el-table-column> |
| | | <el-table-column prop="berth" label="åé æ³ä½" width="120"></el-table-column> |
| | | <el-table-column prop="team" label="TD" min-width="130"></el-table-column> |
| | | <el-table-column prop="team" label="rk_TD" min-width="130"></el-table-column> |
| | | <el-table-column prop="total" label="å·¥ç¨æ»æ°" align="center"></el-table-column> |
| | | <el-table-column prop="cancelled" label="éé¡¹æ»æ°" align="center"></el-table-column> |
| | | <el-table-column prop="major" label="é大æ
é" align="center"></el-table-column> |
| | |
| | | return [ |
| | | { label: 'å·¥ç¨æ»æ°', value: this.overview.engineeringTotal || 0, note: 'å½åæ
éä»»å¡' }, |
| | | { label: 'é大æ
é', value: this.overview.majorFault || 0, note: 'ééç¹å
³æ³¨' }, |
| | | { label: 'æ¶Hç»è®¡', value: this.overview.hRelated || 0, note: 'æææ è¯ä»»å¡' }, |
| | | { label: 'æ¶rk_Hç»è®¡', value: this.overview.hRelated || 0, note: 'æææ è¯ä»»å¡' }, |
| | | { label: 'æªå®å·¥', value: this.overview.unfinished || 0, note: '派工åå£å¾' }, |
| | | { label: 'å®å·¥å¾
è¯éª', value: this.overview.waitTest || 0, note: 'å¾
è¯éªå·¥å' }, |
| | | { label: 'å·²å®å·¥', value: this.overview.completed || 0, note: 'å¯è¿å
¥éªæ¶' } |
| | |
| | | </el-form-item> |
| | | <el-button type="primary" class="login-btn" :loading="loading" @click="submit">ç»å½ç³»ç»</el-button> |
| | | </el-form> |
| | | <div class="login-tip">第ä¸çæä¸å¯ç¨è§è²æ§å¶ï¼ä½¿ç¨ admin / admin123 ä½éªå
¨é¨åè½ã</div> |
| | | <div class="login-tip">请使ç¨å·²åé
çç³»ç»è´¦å·ç»å½ã</div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | form: { username: 'admin', password: 'admin123' }, |
| | | form: { username: '', password: '' }, |
| | | rules: { |
| | | username: [{ required: true, message: '请è¾å
¥ç¨æ·å', trigger: 'blur' }], |
| | | password: [{ required: true, message: '请è¾å
¥å¯ç ', trigger: 'blur' }] |
| | |
| | | sessionStorage.setItem('username', res.data.username) |
| | | sessionStorage.setItem('realName', res.data.realName) |
| | | sessionStorage.setItem('deptName', res.data.deptName || '') |
| | | sessionStorage.setItem('roles', JSON.stringify(res.data.roles || [])) |
| | | sessionStorage.setItem('permissions', JSON.stringify(res.data.permissions || [])) |
| | | this.$router.push('/dashboard') |
| | | }).finally(() => { this.loading = false }) |
| | | }) |
| | |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | | <el-input v-model="keyword" placeholder="派工åç¼å· / Xå· / æ¿ä¿®åä½" clearable style="width:280px" @keyup.enter.native="load"></el-input> |
| | | <el-input v-model="keyword" placeholder="派工åç¼å· / rk_Xå· / æ¿ä¿®åä½" clearable style="width:280px" @keyup.enter.native="load"></el-input> |
| | | <el-button type="primary" icon="el-icon-search" @click="load">æ¥è¯¢</el-button> |
| | | <div class="spacer"></div> |
| | | <el-tag type="info" size="small">ä»
å®å·¥ç¶æç派工åå¯è¿å
¥éªæ¶æµç¨</el-tag> |
| | |
| | | <el-table-column prop="status" label="éªæ¶ç¶æ" width="155" fixed> |
| | | <template slot-scope="{ row }"><el-tag size="mini" :type="acceptanceMeta(row.status).type">{{ acceptanceMeta(row.status).text }}</el-tag></template> |
| | | </el-table-column> |
| | | <el-table-column prop="xNo" label="Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="xNo" label="rk_Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="contractorName" label="æ¿ä¿®åä½" min-width="170"></el-table-column> |
| | | <el-table-column prop="stage" label="å½åç¯è" width="150"> |
| | | <template slot-scope="{ row }">{{ stageText(row.stage) }}</template> |
| | |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>å®å·¥æ
åµå¤ç</h2> |
| | | <p>æ¯ä¸è¡ä»£è¡¨ä¸å¼ 派工åï¼åªå
许å¾éä¸è¡ï¼å®å·¥ç¶æç±ä¸å¡æé®ç»´æ¤</p> |
| | | <p>æ¯ä¸è¡ä»£è¡¨ä¸å¼ 派工åï¼åªå
许å¾éä¸è¡ï¼ç¹âå®å·¥âåªæ´æ°å®å·¥ç¶æï¼å®å·¥ä¿¡æ¯ç±æ¿ä¿®åä½ç¨æ·å¦è¡å¡«æ¥</p> |
| | | </div> |
| | | <el-button icon="el-icon-refresh" @click="load">å·æ°</el-button> |
| | | </div> |
| | |
| | | </div> |
| | | |
| | | <div class="toolbar action-toolbar"> |
| | | <el-button type="success" icon="el-icon-finished" :disabled="!selectedRow || selectedRow.completionStatus === 'COMPLETED'" @click="goCompletion">å®å·¥</el-button> |
| | | <el-button type="success" icon="el-icon-finished" :disabled="!selectedRow || selectedRow.completionStatus === 'COMPLETED'" @click="setStatus('COMPLETED')">å®å·¥</el-button> |
| | | <el-button type="primary" icon="el-icon-time" :disabled="!canSetStatus" @click="setStatus('WAIT_TEST')">å®å·¥å¾
è¯éª</el-button> |
| | | <el-button icon="el-icon-refresh-left" :disabled="!canSetStatus" @click="setStatus('UNFINISHED')">æªå®å·¥</el-button> |
| | | <el-button type="warning" icon="el-icon-box" :disabled="!canSetStatus" @click="setStatus('WAIT_PARTS')">å¾
å¤ä»¶</el-button> |
| | | <el-button type="primary" plain icon="el-icon-star-on" :disabled="!selectedRow" @click="openScore">Tåè¯å</el-button> |
| | | <el-button type="primary" plain icon="el-icon-star-on" :disabled="!selectedRow" @click="openScore">rk_Tåè¯å</el-button> |
| | | </div> |
| | | |
| | | <div class="table-panel"> |
| | |
| | | <el-table-column prop="completionStatus" label="å®å·¥ç¶æ" width="115" fixed> |
| | | <template slot-scope="{ row }"><el-tag size="mini" :type="completionMeta(row.completionStatus).type">{{ completionMeta(row.completionStatus).text }}</el-tag></template> |
| | | </el-table-column> |
| | | <el-table-column prop="xNo" label="Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="è£
å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="xNo" label="rk_Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="设å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="contractorName" label="æ´¾ååä½" min-width="160"></el-table-column> |
| | | <el-table-column prop="engineeringMajor" label="å·¥ç¨ä¸ä¸" width="100"></el-table-column> |
| | | <el-table-column prop="teamMajor" label="Téä¸ä¸" width="100"></el-table-column> |
| | | <el-table-column prop="teamMajor" label="rk_Téä¸ä¸" width="100"></el-table-column> |
| | | <el-table-column prop="parentSystem" label="æå±ä¸çº§ç³»ç»" min-width="130"></el-table-column> |
| | | <el-table-column prop="reportUnit" label="䏿¥åä½" min-width="120"></el-table-column> |
| | | <el-table-column prop="reportDate" label="䏿¥æ¶é´" width="110"></el-table-column> |
| | | <el-table-column prop="reviewTime" label="XCBå®¡æ ¸æ¶é´" width="160"> |
| | | <el-table-column prop="reviewTime" label="rk_XCBå®¡æ ¸æ¶é´" width="160"> |
| | | <template slot-scope="{ row }">{{ formatTime(row.reviewTime) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="owner" label="è´è´£äºº" width="90"></el-table-column> |
| | |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-dialog title="Tåè¯å" :visible.sync="scoreVisible" width="520px" append-to-body> |
| | | <el-dialog title="rk_Tåè¯å" :visible.sync="scoreVisible" width="520px" append-to-body> |
| | | <el-form :model="scoreForm" label-width="100px"> |
| | | <el-form-item label="派工å"><b>{{ scoreForm.workOrderNo }}</b></el-form-item> |
| | | <el-form-item label="ä¿®åè´¨éè¯å"><el-rate v-model="scoreForm.score" show-text :texts="['å¾å·®','è¾å·®','ä¸è¬','è¯å¥½','ä¼ç§']"></el-rate></el-form-item> |
| | |
| | | this.$message.success('å®å·¥ç¶æå·²æ´æ°') |
| | | this.load() |
| | | }).catch(() => {}) |
| | | }, |
| | | goCompletion() { |
| | | this.$router.push({ path: '/completion/report', query: { workOrderId: this.selectedRow.id } }) |
| | | }, |
| | | openScore() { |
| | | this.scoreForm = { |
| | |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>å®å·¥ä¿¡æ¯ä¸æ¥</h2> |
| | | <p>ä»å®å·¥æ
åµå¤ç页ç¹âå®å·¥âè¿å
¥ï¼æäº¤æååæ´¾å·¥åå®å·¥ç¶æå为âå®å·¥âï¼å¹¶è¿å
¥éªæ¶ç³è¯·</p> |
| | | <p>ä»
æ¾ç¤ºå·²å¨å®å·¥æ
åµå¤çä¸ç¡®è®¤å®å·¥ãåé
ç»æ¬åä½ä¸å°æªå¡«æ¥ç派工åï¼æäº¤åè¿å
¥å®å·¥éªæ¶æµç¨</p> |
| | | </div> |
| | | <el-button icon="el-icon-back" @click="$router.push('/fault/completion')">è¿åå®å·¥å¤ç</el-button> |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | | <span>派工å</span> |
| | | <el-select v-model="form.workOrderId" filterable placeholder="è¯·éæ©æ´¾å·¥å" style="width:360px" :disabled="!!routeWorkOrderId" @change="loadSelected"> |
| | | <el-option v-for="item in selectableOrders" :key="item.id" :label="item.workOrderNo + ' - ' + item.contractorName" :value="item.id"></el-option> |
| | | <el-select v-model="form.workOrderId" filterable placeholder="è¯·éæ©æ´¾å·¥å" style="width:560px" :disabled="!!routeWorkOrderId" no-data-text="没æå¯å¡«æ¥ç派工åï¼é已确认å®å·¥ãåé
ç»æ¬åä½ä¸å°æªå¡«æ¥ï¼" @change="loadSelected"> |
| | | <el-option v-for="item in selectableOrders" :key="item.id" :label="item.workOrderNo + ' - ' + (item.engineeringNameDesc || 'æ å·¥ç¨åç§°') + ' - ' + item.contractorName" :value="item.id"></el-option> |
| | | </el-select> |
| | | <div class="spacer"></div> |
| | | <el-tag v-if="workOrder" size="small" type="info">{{ workOrder.xNo }} · {{ workOrder.equipmentName }}</el-tag> |
| | |
| | | <section class="form-section"> |
| | | <h3>åºç¡ä¿¡æ¯</h3> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="8"><el-form-item label="Xå·"><el-input v-model="form.xNo" readonly></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="rk_Xå·"><el-input v-model="form.xNo" readonly></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="æ¿ä¿®åä½"><el-input v-model="form.contractorName" readonly></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="å·¥ç¨ç¼å·"><el-input v-model="form.engineeringNo"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="å¼å·¥æ¶é´"><el-date-picker v-model="form.startDate" type="date" value-format="yyyy-MM-dd" style="width:100%"></el-date-picker></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="å®å·¥æ¶é´"><el-date-picker v-model="form.finishDate" type="date" value-format="yyyy-MM-dd" style="width:100%"></el-date-picker></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="æ¿ä¿®äºº"><el-input v-model="form.contractorPerson"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="Tå
æ½å·¥"><el-input v-model="form.tInternalConstruction"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="rk_Tå
æ½å·¥"><el-input v-model="form.tInternalConstruction"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="çµè¿å·¥ç¨"><el-input v-model="form.relatedProject"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="å®å·¥èæ¶(天)"><el-input-number v-model="form.durationDays" :min="0" style="width:100%"></el-input-number></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="æ¿ä¿®äººæ°"><el-input-number v-model="form.personCount" :min="0" style="width:100%"></el-input-number></el-form-item></el-col> |
| | |
| | | } |
| | | }, |
| | | loadSelectable() { |
| | | api.get('/work-orders', { params: { status: 'UNFINISHED,WAIT_PARTS,WAIT_TEST' } }).then(res => { this.selectableOrders = res.data || [] }) |
| | | api.get('/work-orders', { params: { completionReportable: true } }).then(res => { this.selectableOrders = res.data || [] }) |
| | | }, |
| | | loadSelected() { |
| | | if (!this.form.workOrderId) return |
| | |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>XCBå©çæ´¾å</h2> |
| | | <p>æ¾ç¤ºJCCå¤é¿å®¡æ¹å®æä¸æªæ´¾åçæ
éï¼ææ¿ä¿®åä½ä¸é®çææ´¾å·¥å</p> |
| | | <h2>rk_XCBå©çæ´¾å</h2> |
| | | <p>æ¾ç¤ºrk_JCCå¤é¿å®¡æ¹å®æä¸æªæ´¾åçæ
éï¼ææ¿ä¿®åä½ä¸é®çææ´¾å·¥å</p> |
| | | </div> |
| | | <el-button type="primary" icon="el-icon-s-promotion" :disabled="selection.length === 0" @click="dispatch">ä¸é®æ´¾å</el-button> |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | | <el-input v-model="keyword" placeholder="æ
éç¼å· / è£
å¤åç§° / å·¥ç¨åç§°åæåæ
åµ" clearable style="width:320px" @keyup.enter.native="load"></el-input> |
| | | <el-input v-model="keyword" placeholder="æ
éç¼å· / 设å¤åç§° / å·¥ç¨åç§°åæåæ
åµ" clearable style="width:320px" @keyup.enter.native="load"></el-input> |
| | | <el-button type="primary" icon="el-icon-search" @click="load">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh" @click="load">å·æ°</el-button> |
| | | <div class="spacer"></div> |
| | |
| | | <el-table :data="rows" border stripe height="calc(100vh - 325px)" @selection-change="selection = $event"> |
| | | <el-table-column type="selection" width="46"></el-table-column> |
| | | <el-table-column prop="faultCode" label="æ
éç¼å·" width="180" fixed></el-table-column> |
| | | <el-table-column prop="xNo" label="Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="è£
å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="xNo" label="rk_Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="设å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="engineeringMajor" label="å·¥ç¨ä¸ä¸" width="100"></el-table-column> |
| | | <el-table-column prop="parentSystem" label="æå±ä¸çº§ç³»ç»" min-width="130"></el-table-column> |
| | | <el-table-column prop="contractorNames" label="æ¿ä¿®åä½" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="reportUnit" label="䏿¥åä½" min-width="120"></el-table-column> |
| | | <el-table-column prop="reportDate" label="䏿¥æ¶é´" width="110"></el-table-column> |
| | | <el-table-column prop="xcbReviewTime" label="XCBå®¡æ ¸æ¶é´" width="160"> |
| | | <el-table-column prop="xcbReviewTime" label="rk_XCBå®¡æ ¸æ¶é´" width="160"> |
| | | <template slot-scope="{ row }">{{ formatTime(row.xcbReviewTime) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="teamMajor" label="Téä¸ä¸" 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="requestedFinishDate" label="è¦æ±å®å·¥æ¥æ" width="120"></el-table-column> |
| | | <el-table-column prop="faultImpact" label="æ
éå½±å" width="100"></el-table-column> |
| | |
| | | <el-table-column prop="workOrderNo" label="派工åç¼å·" width="160"></el-table-column> |
| | | <el-table-column prop="faultCode" label="æ
éç¼å·" width="180"></el-table-column> |
| | | <el-table-column prop="contractorName" label="æ¿ä¿®åä½" min-width="180"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="è£
å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="equipmentName" label="设å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="completionStatus" label="å®å·¥ç¶æ" width="100"> |
| | | <template slot-scope="{ row }"><el-tag size="mini" type="info">{{ completionMeta(row.completionStatus).text }}</el-tag></template> |
| | | </el-table-column> |
| | |
| | | <el-col :span="8"><el-form-item label="æ
éç¼å·"><el-input v-model="form.faultCode" readonly placeholder="ä¿ååèªå¨çæ"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="䏿¥åä½"><el-input v-model="form.reportUnit"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="䏿¥æ¥æ"><el-date-picker v-model="form.reportDate" type="date" value-format="yyyy-MM-dd" style="width:100%"></el-date-picker></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="Xå·"><el-select v-model="form.xNo" filterable placeholder="è¯·éæ©Xå·" style="width:100%"><el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="rk_Xå·"><el-select v-model="form.xNo" filterable placeholder="è¯·éæ©rk_Xå·" style="width:100%"><el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="è´è´£äºº"><el-input v-model="form.owner"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="Téä¸ä¸"><el-select v-model="form.teamMajor" style="width:100%"><el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="rk_Téä¸ä¸"><el-select v-model="form.teamMajor" style="width:100%"><el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="å·¥ç¨ä¸ä¸"><el-select v-model="form.engineeringMajor" style="width:100%"><el-option v-for="item in engineeringMajors" :key="item.id" :label="item.name" :value="item.name"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="16"> |
| | | <el-form-item label="è£
å¤åç§°" prop="equipmentName"> |
| | | <el-form-item label="设å¤åç§°" prop="equipmentName"> |
| | | <el-input v-model="form.equipmentName" readonly :title="form.equipmentName" placeholder="ç¹å»å³ä¾§æé®ä»äº§åç»ææ é设å¤ï¼å¯å¤é"> |
| | | <el-button slot="append" icon="el-icon-s-grid" @click="pickerVisible = true">éæ©è£
å¤</el-button> |
| | | <el-button slot="append" icon="el-icon-s-grid" @click="pickerVisible = true">éæ©è®¾å¤</el-button> |
| | | </el-input> |
| | | <div v-if="selectedEquipmentIds.length" class="field-tip">å·²é {{ selectedEquipmentIds.length }} å°è®¾å¤</div> |
| | | </el-form-item> |
| | |
| | | <el-col :span="8"><el-form-item label="æ¯å¦èªä¿®"><el-switch v-model="form.isSelfRepair" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col> |
| | | </el-row> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="8"><el-form-item label="æ¯å¦æ¶H"><el-switch v-model="form.isHRelated" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="æ¯å¦æ¶rk_H"><el-switch v-model="form.isHRelated" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="æ¯å¦çæµé¡¹ç®"><el-switch v-model="form.isMonitorItem" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="çæµæ°å¼"><el-input v-model="form.monitorValue" :disabled="form.isMonitorItem !== 1"></el-input></el-form-item></el-col> |
| | | <el-col :span="24"><el-form-item label="é大æ
éæè¿°"><el-input v-model="form.majorFaultDesc" type="textarea" :rows="2"></el-input></el-form-item></el-col> |
| | |
| | | natures: ['ä¸è¬', 'é大'], |
| | | form: {}, |
| | | rules: { |
| | | equipmentName: [{ required: true, message: 'è¯·éæ©è£
å¤åç§°', trigger: 'change' }], |
| | | equipmentName: [{ required: true, message: 'è¯·éæ©è®¾å¤åç§°', trigger: 'change' }], |
| | | engineeringNameDesc: [{ required: true, message: '请填åå·¥ç¨åç§°åæåæ
åµ', trigger: 'blur' }] |
| | | } |
| | | } |
| | |
| | | api.get('/system/engineering-majors').then(res => { this.engineeringMajors = res.data || [] }) |
| | | }, |
| | | loadXNumbers() { |
| | | api.get('/system/tree', { params: { type: 'product' } }).then(res => { |
| | | const values = [] |
| | | const walk = nodes => { |
| | | if (!nodes) return |
| | | nodes.forEach(node => { |
| | | if (node.nodeType === 'X_NO' || node.nodeLevel === 2) values.push(node.name) |
| | | walk(node.children) |
| | | }) |
| | | } |
| | | walk(res.data) |
| | | this.xNumbers = Array.from(new Set(values.filter(Boolean))).sort((a, b) => a.localeCompare(b, 'zh-CN')) |
| | | }) |
| | | api.get('/system/tree/product/x-numbers').then(res => { this.xNumbers = res.data || [] }) |
| | | }, |
| | | onEquipmentSelected(nodes) { |
| | | const selected = Array.isArray(nodes) ? nodes : [nodes] |
| | |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>æ
é䏿¥</h2> |
| | | <p>Tåå½å
¥è£
å¤ä¿¡æ¯åä¿®çè¦æ±ï¼æ
éç¼å·ä¿ååæè§åèªå¨çæ</p> |
| | | <p>rk_Tåå½å
¥è®¾å¤ä¿¡æ¯åä¿®çè¦æ±ï¼æ
éç¼å·ä¿ååæè§åèªå¨çæ</p> |
| | | </div> |
| | | <el-button icon="el-icon-plus" type="primary" @click="openCreate">æ°å¢æ
é</el-button> |
| | | </div> |
| | | |
| | | <div class="toolbar query-toolbar"> |
| | | <span class="filter-label">Xå·</span> |
| | | <span class="filter-label">rk_Xå·</span> |
| | | <el-select v-model="query.xNo" placeholder="å
¨é¨" clearable style="width:100px"> |
| | | <el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option> |
| | | </el-select> |
| | | <span class="filter-label">Téä¸ä¸</span> |
| | | <span class="filter-label">rk_Téä¸ä¸</span> |
| | | <el-select v-model="query.teamMajor" placeholder="å
¨é¨" clearable style="width:130px"> |
| | | <el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option> |
| | | </el-select> |
| | |
| | | <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-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> |
| | |
| | | <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="Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="è£
å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="xNo" label="rk_Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="设å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="parentSystem" label="æå±ä¸çº§ç³»ç»" min-width="120"></el-table-column> |
| | | <el-table-column prop="engineeringNameDesc" label="å·¥ç¨åç§°åæåæ
åµ" min-width="210" 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="Téä¸ä¸" 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 label="æ¯å¦èªä¿®" width="90" align="center"> |
| | | <template slot-scope="{ row }">{{ yesNo(row.isSelfRepair) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¯å¦æ¶H" width="90" align="center"> |
| | | <el-table-column label="æ¯å¦æ¶rk_H" width="90" align="center"> |
| | | <template slot-scope="{ row }">{{ yesNo(row.isHRelated) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="çæµé¡¹ç®" width="90" align="center"> |
| | |
| | | <el-col :span="8"><el-form-item label="æ
éç¼å·"><el-input v-model="form.faultCode" readonly placeholder="ä¿ååèªå¨çæ"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="䏿¥åä½"><el-input v-model="form.reportUnit"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="䏿¥æ¥æ"><el-date-picker v-model="form.reportDate" type="date" value-format="yyyy-MM-dd" style="width:100%"></el-date-picker></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="Xå·"><el-select v-model="form.xNo" filterable placeholder="è¯·éæ©Xå·" style="width:100%"><el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="rk_Xå·"><el-select v-model="form.xNo" filterable placeholder="è¯·éæ©rk_Xå·" style="width:100%"><el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="è´è´£äºº"><el-input v-model="form.owner"></el-input></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="Téä¸ä¸"><el-select v-model="form.teamMajor" style="width:100%"><el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="rk_Téä¸ä¸"><el-select v-model="form.teamMajor" style="width:100%"><el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="å·¥ç¨ä¸ä¸"><el-select v-model="form.engineeringMajor" style="width:100%"><el-option v-for="item in engineeringMajors" :key="item.id" :label="item.name" :value="item.name"></el-option></el-select></el-form-item></el-col> |
| | | <el-col :span="16"> |
| | | <el-form-item label="è£
å¤åç§°" prop="equipmentName"> |
| | | <el-form-item label="设å¤åç§°" prop="equipmentName"> |
| | | <el-input v-model="form.equipmentName" readonly :title="form.equipmentName" placeholder="ç¹å»å³ä¾§æé®ä»äº§åç»ææ é设å¤ï¼å¯å¤é"> |
| | | <el-button slot="append" icon="el-icon-s-grid" @click="pickerVisible = true">éæ©è£
å¤</el-button> |
| | | <el-button slot="append" icon="el-icon-s-grid" @click="pickerVisible = true">éæ©è®¾å¤</el-button> |
| | | </el-input> |
| | | <div v-if="selectedEquipmentIds.length" class="field-tip">å·²é {{ selectedEquipmentIds.length }} å°è®¾å¤</div> |
| | | </el-form-item> |
| | |
| | | <el-col :span="8"><el-form-item label="æ¯å¦èªä¿®"><el-switch v-model="form.isSelfRepair" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col> |
| | | </el-row> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="8"><el-form-item label="æ¯å¦æ¶H"><el-switch v-model="form.isHRelated" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="æ¯å¦æ¶rk_H"><el-switch v-model="form.isHRelated" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="æ¯å¦çæµé¡¹ç®"><el-switch v-model="form.isMonitorItem" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col> |
| | | <el-col :span="8"><el-form-item label="çæµæ°å¼"><el-input v-model="form.monitorValue" :disabled="form.isMonitorItem !== 1"></el-input></el-form-item></el-col> |
| | | <el-col :span="24"><el-form-item label="é大æ
éæè¿°"><el-input v-model="form.majorFaultDesc" type="textarea" :rows="2"></el-input></el-form-item></el-col> |
| | |
| | | natures: ['ä¸è¬', 'é大'], |
| | | form: {}, |
| | | rules: { |
| | | equipmentName: [{ required: true, message: 'è¯·éæ©è£
å¤åç§°', trigger: 'change' }], |
| | | equipmentName: [{ required: true, message: 'è¯·éæ©è®¾å¤åç§°', trigger: 'change' }], |
| | | engineeringNameDesc: [{ required: true, message: '请填åå·¥ç¨åç§°åæåæ
åµ', trigger: 'blur' }] |
| | | } |
| | | } |
| | |
| | | }) |
| | | }, |
| | | loadXNumbers() { |
| | | api.get('/system/tree', { params: { type: 'product' } }).then(res => { |
| | | const values = [] |
| | | const walk = nodes => { |
| | | if (!nodes) return |
| | | nodes.forEach(node => { |
| | | if (node.nodeType === 'X_NO' || node.nodeLevel === 2) values.push(node.name) |
| | | walk(node.children) |
| | | }) |
| | | } |
| | | walk(res.data) |
| | | this.xNumbers = Array.from(new Set(values.filter(Boolean))).sort((a, b) => a.localeCompare(b, 'zh-CN')) |
| | | }) |
| | | api.get('/system/tree/product/x-numbers').then(res => { this.xNumbers = res.data || [] }) |
| | | }, |
| | | queryParams() { |
| | | const range = this.query.reportDateRange || [] |
| | |
| | | reportUnit: sessionStorage.getItem('deptName') || '', |
| | | reportDate: today, |
| | | xNo: '', |
| | | owner: sessionStorage.getItem('realName') || 'Tå', |
| | | owner: sessionStorage.getItem('realName') || 'rk_Tå', |
| | | teamMajor: (this.teamMajors[0] || {}).name || '', |
| | | engineeringMajor: (this.engineeringMajors[0] || {}).name || '', |
| | | equipmentIds: '', |
| | |
| | | exportCsv() { |
| | | api.get('/faults', { params: this.queryParams() }).then(res => { |
| | | const rows = res.data || [] |
| | | const headers = ['æ
éç¼å·', 'Xå·', 'è£
å¤åç§°', 'ä»»å¡ç¶æ', 'å·¥ç¨åç§°åæåæ
åµ', '䏿¥åä½', '䏿¥æ¥æ'] |
| | | const headers = ['æ
éç¼å·', 'rk_Xå·', '设å¤åç§°', 'ä»»å¡ç¶æ', 'å·¥ç¨åç§°åæåæ
åµ', '䏿¥åä½', '䏿¥æ¥æ'] |
| | | const lines = [headers.join(',')].concat(rows.map(row => [ |
| | | row.faultCode, row.xNo, row.equipmentName, this.taskMeta(row.taskStatus).text, |
| | | row.engineeringNameDesc, row.reportUnit, row.reportDate |
| | |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | | <el-input v-model="keyword" placeholder="æ
éç¼å· / è£
å¤åç§° / å·¥ç¨åç§°åæåæ
åµ" clearable style="width:320px" @keyup.enter.native="load"></el-input> |
| | | <el-input v-model="keyword" placeholder="æ
éç¼å· / 设å¤åç§° / å·¥ç¨åç§°åæåæ
åµ" clearable style="width:320px" @keyup.enter.native="load"></el-input> |
| | | <el-button icon="el-icon-search" type="primary" @click="load">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh" @click="load">å·æ°</el-button> |
| | | <div class="spacer"></div> |
| | |
| | | <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="Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="è£
å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="xNo" label="rk_Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="设å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="parentSystem" label="æå±ä¸çº§ç³»ç»" min-width="120"></el-table-column> |
| | | <el-table-column prop="engineeringNameDesc" label="å·¥ç¨åç§°åæåæ
åµ" min-width="210" 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="Téä¸ä¸" 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 label="æ¯å¦èªä¿®" width="90" align="center"> |
| | | <template slot-scope="{ row }">{{ yesNo(row.isSelfRepair) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¯å¦æ¶H" width="90" align="center"> |
| | | <el-table-column label="æ¯å¦æ¶rk_H" width="90" align="center"> |
| | | <template slot-scope="{ row }">{{ yesNo(row.isHRelated) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="çæµé¡¹ç®" width="90" align="center"> |
| | |
| | | <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="审æ¹ä¸éè¿æ¶ç´æ¥éåXCBå©çï¼ä»»å¡ç¶æå为âä¸çº§æåâã"></el-alert> |
| | | <el-alert v-if="form.result === 'REJECT'" :closable="false" type="warning" show-icon title="审æ¹ä¸éè¿æ¶ç´æ¥éårk_XCBå©çï¼ä»»å¡ç¶æå为âä¸çº§æåâã"></el-alert> |
| | | </el-form> |
| | | <template slot="footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | |
| | | }, |
| | | computed: { |
| | | pageTitle() { |
| | | return { CMB: 'CMBé¢å¯¼å®¡æ¹', JCC_ASSISTANT: 'JCCå©ç审æ¹', JCC_DIRECTOR: 'JCCå¤é¿å®¡æ¹' }[this.stage] |
| | | return { CMB: 'rk_CMBé¢å¯¼å®¡æ¹', JCC_ASSISTANT: 'rk_JCCå©ç审æ¹', JCC_DIRECTOR: 'rk_JCCå¤é¿å®¡æ¹' }[this.stage] |
| | | }, |
| | | subtitle() { |
| | | return { |
| | | CMB: '对XCBå©çå®¡æ ¸å®æçæ
éè¿è¡æ¹ééè¿æéå', |
| | | JCC_ASSISTANT: '对CMBé¢å¯¼å®¡æ¹å®æçæ
éè¿è¡æ¹ééè¿æéå', |
| | | JCC_DIRECTOR: '对JCCå©ç审æ¹å®æçæ
éè¿è¡æç»å®¡æ¹ï¼éè¿åè¿å
¥æ´¾å' |
| | | CMB: '对rk_XCBå©çå®¡æ ¸å®æçæ
éè¿è¡æ¹ééè¿æéå', |
| | | JCC_ASSISTANT: '对rk_CMBé¢å¯¼å®¡æ¹å®æçæ
éè¿è¡æ¹ééè¿æéå', |
| | | JCC_DIRECTOR: '对rk_JCCå©ç审æ¹å®æçæ
éè¿è¡æç»å®¡æ¹ï¼éè¿åè¿å
¥æ´¾å' |
| | | }[this.stage] |
| | | }, |
| | | status() { |
| | |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>XCBå©çå®¡æ ¸</h2> |
| | | <p>åé
æ¿ä¿®åä½ãè·è¸ªææ¯éåè¦æ±å®å·¥æ¥æï¼å®¡æ ¸åè¿å
¥CMBé¢å¯¼å®¡æ¹</p> |
| | | <h2>rk_XCBå©çå®¡æ ¸</h2> |
| | | <p>åé
æ¿ä¿®åä½ãè·è¸ªææ¯éåè¦æ±å®å·¥æ¥æï¼å®¡æ ¸åè¿å
¥rk_CMBé¢å¯¼å®¡æ¹</p> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="toolbar query-toolbar"> |
| | | <span class="filter-label">Xå·</span> |
| | | <span class="filter-label">rk_Xå·</span> |
| | | <el-select v-model="query.xNo" placeholder="å
¨é¨" clearable style="width:100px"> |
| | | <el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option> |
| | | </el-select> |
| | | <span class="filter-label">Téä¸ä¸</span> |
| | | <span class="filter-label">rk_Téä¸ä¸</span> |
| | | <el-select v-model="query.teamMajor" placeholder="å
¨é¨" clearable style="width:130px"> |
| | | <el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option> |
| | | </el-select> |
| | |
| | | <el-select v-model="query.status" placeholder="å
¨é¨" clearable style="width:180px"> |
| | | <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="load"></el-input> |
| | | <el-input v-model="query.keyword" placeholder="æ
éç¼å·/设å¤åç§°/å·¥ç¨åç§°åæåæ
åµ" clearable style="width:320px" @keyup.enter.native="load"></el-input> |
| | | <el-button type="primary" icon="el-icon-search" @click="load">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh-left" @click="resetQuery">éç½®</el-button> |
| | | <el-button icon="el-icon-refresh" @click="load">å·æ°</el-button> |
| | |
| | | <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="Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="è£
å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="xNo" label="rk_Xå·" width="64"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="设å¤åç§°" min-width="210" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="parentSystem" label="æå±ä¸çº§ç³»ç»" min-width="120"></el-table-column> |
| | | <el-table-column prop="engineeringNameDesc" label="å·¥ç¨åç§°åæåæ
åµ" min-width="210" 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="Téä¸ä¸" 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 label="æ¯å¦èªä¿®" width="90" align="center"> |
| | | <template slot-scope="{ row }">{{ yesNo(row.isSelfRepair) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¯å¦æ¶H" width="90" align="center"> |
| | | <el-table-column label="æ¯å¦æ¶rk_H" width="90" align="center"> |
| | | <template slot-scope="{ row }">{{ yesNo(row.isHRelated) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="çæµé¡¹ç®" width="90" align="center"> |
| | |
| | | |
| | | <fault-edit-dialog :visible.sync="editVisible" :fault="editingFault" @saved="onFaultSaved"></fault-edit-dialog> |
| | | |
| | | <el-dialog title="XCBå©çå®¡æ ¸" :visible.sync="dialogVisible" width="820px" append-to-body> |
| | | <el-dialog title="rk_XCBå©çå®¡æ ¸" :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="å·²éæ
é"> |
| | |
| | | api.get('/system/engineering-majors').then(res => { this.engineeringMajors = res.data || [] }) |
| | | }, |
| | | loadXNumbers() { |
| | | api.get('/system/tree', { params: { type: 'product' } }).then(res => { |
| | | const values = [] |
| | | const walk = nodes => { |
| | | if (!nodes) return |
| | | nodes.forEach(node => { |
| | | if (node.nodeType === 'X_NO' || node.nodeLevel === 2) values.push(node.name) |
| | | walk(node.children) |
| | | }) |
| | | } |
| | | walk(res.data) |
| | | this.xNumbers = Array.from(new Set(values.filter(Boolean))).sort((a, b) => a.localeCompare(b, 'zh-CN')) |
| | | }) |
| | | api.get('/system/tree/product/x-numbers').then(res => { this.xNumbers = res.data || [] }) |
| | | }, |
| | | editSelected() { |
| | | if (this.selection.length !== 1) return |
| | |
| | | api.post('/faults/cancel', { |
| | | ids: this.selection.map(item => item.id), |
| | | reason: this.cancelReason.trim(), |
| | | cancelledBy: sessionStorage.getItem('realName') || 'XCBå©ç' |
| | | cancelledBy: sessionStorage.getItem('realName') || 'rk_XCBå©ç' |
| | | }).then(() => { |
| | | this.$message.success('é项宿') |
| | | this.cancelVisible = false |
| | |
| | | <div v-if="type === 'overview'" class="stat-grid"> |
| | | <div class="stat-card"><div class="label">å·¥ç¨æ»æ°</div><div class="value">{{ stats.engineeringTotal || 0 }}</div></div> |
| | | <div class="stat-card"><div class="label">é大æ
é</div><div class="value">{{ stats.majorFault || 0 }}</div></div> |
| | | <div class="stat-card"><div class="label">æ¶Hç»è®¡</div><div class="value">{{ stats.hRelated || 0 }}</div></div> |
| | | <div class="stat-card"><div class="label">æ¶rk_Hç»è®¡</div><div class="value">{{ stats.hRelated || 0 }}</div></div> |
| | | <div class="stat-card"><div class="label">çæµé¡¹ç®</div><div class="value">{{ stats.monitorItem || 0 }}</div></div> |
| | | <div class="stat-card"><div class="label">æªå®å·¥</div><div class="value">{{ stats.unfinished || 0 }}</div></div> |
| | | <div class="stat-card"><div class="label">å·²å®å·¥</div><div class="value">{{ stats.completed || 0 }}</div></div> |
| | |
| | | computed: { |
| | | title() { |
| | | return { |
| | | overview: 'è£
夿
éæ»ä½åæ', |
| | | overview: 'è®¾å¤æ
éæ»ä½åæ', |
| | | 'top-equipment': 'æ
éé«å设å¤Top', |
| | | 'by-team': 'æTç»è®¡', |
| | | 'by-team': 'rk_æTç»è®¡', |
| | | contractors: 'åå®¶ç»´ä¿®æ
åµ', |
| | | trend: 'æ
éæ° / å¤åå¤ä»¶æ¶è年度è¶å¿' |
| | | }[this.type] || 'ç»è®¡æ¥è¡¨' |
| | |
| | | { prop: 'value', label: 'æ
éæ°', width: 100 } |
| | | ] |
| | | if (this.type === 'by-team') return [ |
| | | { prop: 'name', label: 'Téä¸ä¸', width: 150 }, |
| | | { prop: 'name', label: 'rk_Téä¸ä¸', width: 150 }, |
| | | { prop: 'faultCount', label: 'æ
éæ°' }, |
| | | { prop: 'partsCount', label: 'å¤åå¤ä»¶æ¶è' } |
| | | ] |
| | |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>æ¿ä¿®åä½ç®¡ç</h2> |
| | | <p>æåºç¨äºXCBå©çå®¡æ ¸æ¶æ¾ç¤ºå7ä¸ªå¸¸ç¨æ¿ä¿®åä½</p> |
| | | <p>æ°æ®æ¥æºï¼ç»ç»æºæé¨é¨è¡¨ï¼DEPT_TYPE = cjï¼</p> |
| | | </div> |
| | | <el-button type="primary" icon="el-icon-plus" @click="openCreate">æ°å¢æ¿ä¿®åä½</el-button> |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | | <el-input v-model="keyword" placeholder="åç§° / ç®ç§° / è系人" clearable style="width:260px"></el-input> |
| | | <el-button type="primary" icon="el-icon-search" @click="load">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh" @click="load">å·æ°</el-button> |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | | <el-input |
| | | v-model="keyword" |
| | | placeholder="åç§° / é¨é¨ç¼ç / è´è´£äºº" |
| | | clearable |
| | | style="width:280px" |
| | | @keyup.enter.native="load" |
| | | ></el-input> |
| | | <el-button type="primary" icon="el-icon-search" @click="load">æ¥è¯¢</el-button> |
| | | <div class="spacer"></div> |
| | | <span class="summary">å
± {{ filteredRows.length }} 个æ¿ä¿®åä½</span> |
| | | </div> |
| | | |
| | | <div class="table-panel"> |
| | | <el-table :data="filteredRows" border stripe height="calc(100vh - 245px)"> |
| | | <el-table |
| | | v-loading="loading" |
| | | :data="filteredRows" |
| | | border |
| | | stripe |
| | | height="calc(100vh - 245px)" |
| | | > |
| | | <el-table-column prop="sortNo" label="æåº" width="80" align="center"></el-table-column> |
| | | <el-table-column label="常ç¨" width="80" align="center"> |
| | | <template slot-scope="{ $index }"><el-tag v-if="$index < 7" size="mini" type="success">å7</el-tag><span v-else>-</span></template> |
| | | <template slot-scope="{ $index }"> |
| | | <el-tag v-if="$index < 7" size="mini" type="success">å7</el-tag> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="name" label="åç§°" min-width="220"></el-table-column> |
| | | <el-table-column prop="shortName" label="ç®ç§°" width="150"></el-table-column> |
| | | <el-table-column prop="nature" label="å使§è´¨" width="130"></el-table-column> |
| | | <el-table-column prop="contact" label="è系人" width="120"></el-table-column> |
| | | <el-table-column prop="dishonest" label="æ¯å¦å¤±ä¿¡åä½" width="130"> |
| | | <template slot-scope="{ row }"><el-tag size="mini" :type="row.dishonest === 1 ? 'danger' : 'success'">{{ row.dishonest === 1 ? 'æ¯' : 'å¦' }}</el-tag></template> |
| | | </el-table-column> |
| | | <el-table-column prop="remark" label="夿³¨" min-width="180" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="æä½" width="130" fixed="right"> |
| | | <el-table-column prop="name" label="åç§°" min-width="240" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="deptCode" label="é¨é¨ç¼ç " width="160"></el-table-column> |
| | | <el-table-column prop="contact" label="è´è´£äºº" width="120"></el-table-column> |
| | | <el-table-column prop="phone" label="èç³»çµè¯" width="160"></el-table-column> |
| | | <el-table-column label="ç¶æ" width="100" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <el-button type="text" @click="openEdit(row)">ç¼è¾</el-button> |
| | | <el-button type="text" class="danger-link" @click="remove(row)">å é¤</el-button> |
| | | <el-tag size="mini" :type="row.status === '1' ? 'info' : 'success'"> |
| | | {{ row.status === '1' ? 'åç¨' : 'æ£å¸¸' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-dialog :title="form.id ? 'ç¼è¾æ¿ä¿®åä½' : 'æ°å¢æ¿ä¿®åä½'" :visible.sync="dialogVisible" width="620px" append-to-body> |
| | | <el-form ref="form" :model="form" :rules="rules" label-width="110px"> |
| | | <el-form-item label="åç§°" prop="name"><el-input v-model="form.name"></el-input></el-form-item> |
| | | <el-form-item label="ç®ç§°" prop="shortName"><el-input v-model="form.shortName"></el-input></el-form-item> |
| | | <el-form-item label="å使§è´¨"><el-select v-model="form.nature" style="width:100%"><el-option v-for="item in natures" :key="item" :label="item" :value="item"></el-option></el-select></el-form-item> |
| | | <el-form-item label="æåº"><el-input-number v-model="form.sortNo" :min="1" style="width:180px"></el-input-number></el-form-item> |
| | | <el-form-item label="è系人"><el-input v-model="form.contact"></el-input></el-form-item> |
| | | <el-form-item label="æ¯å¦å¤±ä¿¡åä½"><el-switch v-model="form.dishonest" :active-value="1" :inactive-value="0"></el-switch></el-form-item> |
| | | <el-form-item label="夿³¨"><el-input v-model="form.remark" type="textarea" :rows="3"></el-input></el-form-item> |
| | | </el-form> |
| | | <template slot="footer"><el-button @click="dialogVisible = false">åæ¶</el-button><el-button type="primary" :loading="saving" @click="save">ä¿å</el-button></template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | <script> |
| | |
| | | name: 'ContractorManage', |
| | | data() { |
| | | return { |
| | | rows: [], keyword: '', dialogVisible: false, saving: false, |
| | | natures: ['åå®¶', 'æå¡å', '综ååä½', 'Té', 'å
¶ä»'], |
| | | form: {}, |
| | | rules: { |
| | | name: [{ required: true, message: '请è¾å
¥åç§°', trigger: 'blur' }], |
| | | shortName: [{ required: true, message: '请è¾å
¥ç®ç§°', trigger: 'blur' }] |
| | | } |
| | | loading: false, |
| | | rows: [], |
| | | keyword: '' |
| | | } |
| | | }, |
| | | computed: { |
| | | filteredRows() { |
| | | if (!this.keyword) return this.rows |
| | | const text = this.keyword.toLowerCase() |
| | | return this.rows.filter(item => [item.name, item.shortName, item.contact].some(value => String(value || '').toLowerCase().includes(text))) |
| | | const keyword = this.keyword.trim().toLowerCase() |
| | | if (!keyword) return this.rows |
| | | return this.rows.filter(item => [item.name, item.deptCode, item.contact] |
| | | .some(value => String(value || '').toLowerCase().includes(keyword))) |
| | | } |
| | | }, |
| | | created() { this.load() }, |
| | | methods: { |
| | | load() { api.get('/system/contractors').then(res => { this.rows = res.data || [] }) }, |
| | | blank() { return { name: '', shortName: '', nature: 'åå®¶', sortNo: this.rows.length + 1, contact: '', dishonest: 0, remark: '' } }, |
| | | openCreate() { this.form = this.blank(); this.dialogVisible = true }, |
| | | openEdit(row) { this.form = { ...row }; this.dialogVisible = true }, |
| | | save() { |
| | | this.$refs.form.validate(valid => { |
| | | if (!valid) return |
| | | this.saving = true |
| | | const request = this.form.id ? api.put('/system/contractors/' + this.form.id, this.form) : api.post('/system/contractors', this.form) |
| | | request.then(() => { this.$message.success('ä¿åæå'); this.dialogVisible = false; this.load() }).finally(() => { this.saving = false }) |
| | | }) |
| | | created() { |
| | | this.load() |
| | | }, |
| | | remove(row) { |
| | | this.$confirm(`ç¡®å®å é¤â${row.name}âåï¼`, 'å é¤ç¡®è®¤', { type: 'warning' }).then(() => api.delete('/system/contractors/' + row.id)).then(() => { |
| | | this.$message.success('å 餿å'); this.load() |
| | | }).catch(() => {}) |
| | | methods: { |
| | | load() { |
| | | this.loading = true |
| | | api.get('/system/contractors').then(res => { |
| | | this.rows = res.data || [] |
| | | }).finally(() => { |
| | | this.loading = false |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped>.danger-link { color: #d9534f; }</style> |
| | | <style scoped> |
| | | .summary { |
| | | color: #6d7f91; |
| | | font-size: 13px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div><h2>ç»å½æ¥å¿</h2><p>è®°å½æ¯æ¬¡ç»å½å°è¯çç¨æ·ãæ¥æºå°åãç»æåæ¶é´</p></div> |
| | | </div> |
| | | <div class="table-panel"> |
| | | <div class="toolbar"> |
| | | <el-input v-model="filters.keyword" clearable placeholder="è´¦æ·å / å§å" style="width:200px" @keyup.enter.native="search"></el-input> |
| | | <el-select v-model="filters.status" clearable placeholder="ç»å½ç»æ" style="width:140px"> |
| | | <el-option label="æå" value="SUCCESS"></el-option> |
| | | <el-option label="失败" value="FAIL"></el-option> |
| | | </el-select> |
| | | <el-date-picker |
| | | v-model="filters.dates" |
| | | type="daterange" |
| | | value-format="yyyy-MM-dd" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | style="width:260px" |
| | | ></el-date-picker> |
| | | <el-button type="primary" icon="el-icon-search" v-permission="'system:login-logs:query'" @click="search">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh-left" @click="reset">éç½®</el-button> |
| | | </div> |
| | | <el-table v-loading="loading" :data="rows" border stripe height="calc(100vh - 250px)"> |
| | | <el-table-column prop="loginTime" label="ç»å½æ¶é´" width="180"></el-table-column> |
| | | <el-table-column prop="userName" label="è´¦æ·å" min-width="140"></el-table-column> |
| | | <el-table-column prop="nickName" label="å§å" min-width="140"></el-table-column> |
| | | <el-table-column prop="deptName" label="æå±é¨é¨" min-width="180" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="loginIp" label="ç»å½IP" min-width="150"></el-table-column> |
| | | <el-table-column label="ç»æ" width="90" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag size="mini" :type="row.status === 'SUCCESS' ? 'success' : 'danger'">{{ row.status === 'SUCCESS' ? 'æå' : '失败' }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="message" label="说æ" min-width="180" show-overflow-tooltip></el-table-column> |
| | | </el-table> |
| | | <el-pagination |
| | | class="pager" |
| | | background |
| | | layout="total, sizes, prev, pager, next" |
| | | :total="total" |
| | | :current-page="current" |
| | | :page-size="size" |
| | | :page-sizes="[20, 50, 100]" |
| | | @current-change="handleCurrentChange" |
| | | @size-change="handleSizeChange" |
| | | ></el-pagination> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import api from '@/api' |
| | | export default { |
| | | name: 'LoginLogManage', |
| | | data() { |
| | | return { |
| | | rows: [], |
| | | total: 0, |
| | | current: 1, |
| | | size: 20, |
| | | loading: false, |
| | | filters: { keyword: '', status: '', dates: [] } |
| | | } |
| | | }, |
| | | created() { this.load() }, |
| | | methods: { |
| | | load() { |
| | | this.loading = true |
| | | const dates = this.filters.dates || [] |
| | | api.get('/system/login-logs', { |
| | | params: { |
| | | keyword: this.filters.keyword || undefined, |
| | | status: this.filters.status || undefined, |
| | | startDate: dates[0] || undefined, |
| | | endDate: dates[1] || undefined, |
| | | current: this.current, |
| | | size: this.size |
| | | } |
| | | }).then(res => { |
| | | const data = res.data || {} |
| | | this.rows = data.records || [] |
| | | this.total = data.total || 0 |
| | | }).finally(() => { this.loading = false }) |
| | | }, |
| | | search() { this.current = 1; this.load() }, |
| | | reset() { this.filters = { keyword: '', status: '', dates: [] }; this.search() }, |
| | | handleCurrentChange(page) { this.current = page; this.load() }, |
| | | handleSizeChange(size) { this.size = size; this.current = 1; this.load() } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped> |
| | | .toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; } |
| | | .pager { margin-top: 12px; text-align: right; } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div><h2>èå管ç</h2><p>èåä¸ºå¨æé
ç½®ï¼å·¦ä¾§å¯¼èªç´æ¥è¯»åè¿éçæ°æ®</p></div> |
| | | <el-button type="primary" icon="el-icon-plus" @click="openCreate(null)">æ°å¢ä¸çº§èå</el-button> |
| | | <div><h2>èå管ç</h2><p>æ¯æç®å½ãèåãæé®ä¸ç§ç±»åï¼æé®åªè½æå¨èåä¸çº§ï¼ç¨äºé
ç½®é¡µé¢æä½æé</p></div> |
| | | <el-button type="primary" icon="el-icon-plus" v-permission="'system:menus:add'" @click="openCreate(null)">æ°å¢ä¸çº§èå</el-button> |
| | | </div> |
| | | <div class="table-panel"> |
| | | <el-table :data="tree" row-key="id" border default-expand-all :tree-props="{ children: 'children' }"> |
| | | <el-table-column prop="name" label="èååç§°" min-width="220"></el-table-column> |
| | | <el-table-column prop="icon" label="徿 " width="200"><template slot-scope="{ row }"><span class="icon-cell"><i :class="row.icon"></i><span>{{ row.icon }}</span></span></template></el-table-column> |
| | | <el-table-column prop="url" label="è·¯ç±å°å" min-width="240"></el-table-column> |
| | | <el-table-column prop="permissions" label="æéæ è¯" min-width="170"></el-table-column> |
| | | <el-table-column prop="menuType" label="ç±»å" width="90"><template slot-scope="{ row }"><el-tag size="mini" :type="row.menuType === 0 ? 'info' : 'primary'">{{ row.menuType === 0 ? 'ç®å½' : 'èå' }}</el-tag></template></el-table-column> |
| | | <el-table-column prop="icon" label="徿 " width="180"> |
| | | <template slot-scope="{ row }"> |
| | | <span v-if="row.menuType !== 2" class="icon-cell"><i :class="row.icon"></i><span>{{ row.icon }}</span></span> |
| | | <span v-else class="muted">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="url" label="è·¯ç±å°å" min-width="220"> |
| | | <template slot-scope="{ row }"> |
| | | <span v-if="row.menuType !== 2">{{ row.url }}</span> |
| | | <span v-else class="muted">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="permissions" label="æéæ è¯" min-width="200"></el-table-column> |
| | | <el-table-column label="ç±»å" width="90"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag size="mini" :type="typeTag(row.menuType)">{{ typeName(row.menuType) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="sortNo" label="æåº" width="80" align="center"></el-table-column> |
| | | <el-table-column prop="visible" label="æ¾ç¤º" width="80" align="center"><template slot-scope="{ row }"><el-tag size="mini" :type="row.visible === 0 ? 'info' : 'success'">{{ row.visible === 0 ? 'éè' : 'æ¾ç¤º' }}</el-tag></template></el-table-column> |
| | | <el-table-column label="æä½" width="200" fixed="right"><template slot-scope="{ row }"><el-button type="text" @click="openCreate(row)">æ°å¢ä¸çº§</el-button><el-button type="text" @click="openEdit(row)">ç¼è¾</el-button><el-button type="text" class="danger-link" @click="remove(row)">å é¤</el-button></template></el-table-column> |
| | | <el-table-column label="æ¾ç¤º" width="80" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag size="mini" :type="row.visible === 0 ? 'info' : 'success'">{{ row.visible === 0 ? 'éè' : 'æ¾ç¤º' }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="270" fixed="right"> |
| | | <template slot-scope="{ row }"> |
| | | <el-button v-if="row.menuType !== 2" type="text" v-permission="'system:menus:add'" @click="openCreate(row)">æ°å¢ä¸çº§</el-button> |
| | | <el-button v-if="row.menuType === 1" type="text" v-permission="'system:menus:buttons'" @click="openButtons(row)">页颿é®</el-button> |
| | | <el-button type="text" v-permission="'system:menus:edit'" @click="openEdit(row)">ç¼è¾</el-button> |
| | | <el-button type="text" class="danger-link" v-permission="'system:menus:delete'" @click="remove(row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-dialog :title="form.id ? 'ç¼è¾èå' : 'æ°å¢èå'" :visible.sync="dialogVisible" width="640px" append-to-body> |
| | | <el-form ref="form" :model="form" :rules="rules" label-width="105px"> |
| | | <el-form-item label="ä¸çº§èå"><el-input :value="parentName" readonly></el-input></el-form-item> |
| | | <el-form-item label="èåç±»å"> |
| | | <el-radio-group v-model="form.menuType"> |
| | | <el-radio-button v-for="item in typeOptions" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="èååç§°" prop="name"><el-input v-model="form.name"></el-input></el-form-item> |
| | | <el-form-item label="èåç±»å"><el-radio-group v-model="form.menuType"><el-radio-button :label="0">ç®å½</el-radio-button><el-radio-button :label="1">èå</el-radio-button></el-radio-group></el-form-item> |
| | | <el-form-item label="è·¯ç±å°å"><el-input v-model="form.url" placeholder="/fault/report"></el-input></el-form-item> |
| | | <el-form-item label="徿 "><el-input v-model="form.icon" placeholder="el-icon-tickets"></el-input></el-form-item> |
| | | <el-form-item label="æéæ è¯"><el-input v-model="form.permissions"></el-input></el-form-item> |
| | | <el-form-item v-if="form.menuType !== 2" label="è·¯ç±å°å"><el-input v-model="form.url" placeholder="/fault/report"></el-input></el-form-item> |
| | | <el-form-item v-if="form.menuType !== 2" label="徿 "><el-input v-model="form.icon" placeholder="el-icon-tickets"></el-input></el-form-item> |
| | | <el-form-item label="æéæ è¯" :prop="form.menuType === 2 ? 'permissions' : ''"> |
| | | <el-input v-model="form.permissions" :placeholder="form.menuType === 2 ? 'ä¾å¦ fault:report:add' : 'å¯éï¼ä¾å¦ fault-report'"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="æåº"><el-input-number v-model="form.sortNo" :min="1" style="width:160px"></el-input-number></el-form-item> |
| | | <el-form-item label="æ¯å¦æ¾ç¤º"><el-switch v-model="form.visible" :active-value="1" :inactive-value="0"></el-switch></el-form-item> |
| | | </el-form> |
| | | <template slot="footer"><el-button @click="dialogVisible = false">åæ¶</el-button><el-button type="primary" :loading="saving" @click="save">ä¿å</el-button></template> |
| | | </el-dialog> |
| | | |
| | | <el-dialog :title="'页颿é®é
ç½® - ' + (buttonMenu.name || '')" :visible.sync="buttonDialogVisible" width="620px" append-to-body> |
| | | <p class="dialog-tip">ä¸åæé®æ¥èªå端æé®ç®å½ï¼å¾éåèªå¨çææé®ç±»åèåï¼å¯å¨èåæé管ç䏿æç»è§è²ã</p> |
| | | <el-checkbox-group v-model="buttonSelected" class="button-list"> |
| | | <el-checkbox v-for="item in buttonAvailable" :key="item.code" :label="item.code" class="button-item"> |
| | | <span class="button-name">{{ item.name }}</span> |
| | | <span class="button-code">{{ item.code }}</span> |
| | | </el-checkbox> |
| | | </el-checkbox-group> |
| | | <div v-if="!buttonAvailable.length" class="empty-tip">è¯¥é¡µé¢ææªç»è®°å¯ç¨æé®</div> |
| | | <template slot="footer"> |
| | | <el-button @click="buttonDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" :loading="buttonSaving" @click="saveButtons">ä¿åæé®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | |
| | | import api from '@/api' |
| | | export default { |
| | | name: 'MenuManage', |
| | | data() { return { tree: [], flat: [], dialogVisible: false, saving: false, form: {}, rules: { name: [{ required: true, message: '请è¾å
¥èååç§°', trigger: 'blur' }] } } }, |
| | | computed: { parentName() { const parent = this.flat.find(item => item.id === this.form.pid); return parent ? parent.name : 'æ ï¼ä¸çº§èåï¼' } }, |
| | | data() { |
| | | return { |
| | | tree: [], |
| | | flat: [], |
| | | dialogVisible: false, |
| | | saving: false, |
| | | form: {}, |
| | | parent: null, |
| | | rules: { name: [{ required: true, message: '请è¾å
¥èååç§°', trigger: 'blur' }] }, |
| | | buttonDialogVisible: false, |
| | | buttonSaving: false, |
| | | buttonMenu: {}, |
| | | buttonAvailable: [], |
| | | buttonSelected: [] |
| | | } |
| | | }, |
| | | computed: { |
| | | parentName() { |
| | | if (!this.parent) return this.form.pid && this.form.pid !== 0 ? 'ä¸çº§èå' : 'æ ï¼ä¸çº§èåï¼' |
| | | return this.parent.name |
| | | }, |
| | | typeOptions() { |
| | | if (this.parent && this.parent.menuType === 1) { |
| | | return [{ label: 'æé®', value: 2 }] |
| | | } |
| | | if (this.parent && this.parent.menuType === 0) { |
| | | return [{ label: 'ç®å½', value: 0 }, { label: 'èå', value: 1 }] |
| | | } |
| | | if (this.form.id && this.form.menuType === 2) { |
| | | return [{ label: 'æé®', value: 2 }] |
| | | } |
| | | return [{ label: 'ç®å½', value: 0 }, { label: 'èå', value: 1 }] |
| | | } |
| | | }, |
| | | created() { this.load() }, |
| | | methods: { |
| | | load() { Promise.all([api.get('/system/menu/nav'), api.get('/system/menu/list')]).then(([tree, list]) => { this.tree = tree.data || []; this.flat = list.data || [] }) }, |
| | | openCreate(parent) { this.form = { pid: parent ? parent.id : 0, name: '', url: '', icon: 'el-icon-tickets', menuType: parent ? 1 : 0, permissions: '', sortNo: 10, visible: 1 }; this.dialogVisible = true }, |
| | | openEdit(row) { this.form = { ...row }; this.dialogVisible = true }, |
| | | save() { this.$refs.form.validate(valid => { if (!valid) return; this.saving = true; const request = this.form.id ? api.put('/system/menu/' + this.form.id, this.form) : api.post('/system/menu', this.form); request.then(() => { this.$message.success('ä¿åæå'); this.dialogVisible = false; this.load() }).finally(() => { this.saving = false }) }) }, |
| | | remove(row) { this.$confirm(`ç¡®å®å é¤èåâ${row.name}âåï¼`, 'å é¤ç¡®è®¤', { type: 'warning' }).then(() => api.delete('/system/menu/' + row.id)).then(() => { this.$message.success('å 餿å'); this.load() }).catch(() => {}) } |
| | | load() { |
| | | Promise.all([api.get('/system/menu/tree'), api.get('/system/menu/list')]).then(([tree, list]) => { |
| | | this.tree = tree.data || [] |
| | | this.flat = list.data || [] |
| | | }) |
| | | }, |
| | | typeName(type) { |
| | | return type === 2 ? 'æé®' : type === 0 ? 'ç®å½' : 'èå' |
| | | }, |
| | | typeTag(type) { |
| | | return type === 2 ? 'warning' : type === 0 ? 'info' : 'primary' |
| | | }, |
| | | openCreate(parent) { |
| | | this.parent = parent || null |
| | | const menuType = parent && parent.menuType === 1 ? 2 : parent ? 1 : 0 |
| | | this.form = { |
| | | pid: parent ? parent.id : 0, |
| | | name: '', |
| | | url: '', |
| | | icon: menuType === 2 ? '' : 'el-icon-tickets', |
| | | menuType, |
| | | permissions: '', |
| | | sortNo: 10, |
| | | visible: 1 |
| | | } |
| | | this.dialogVisible = true |
| | | }, |
| | | openEdit(row) { |
| | | this.parent = this.flat.find(item => item.id === row.pid) || null |
| | | this.form = { ...row } |
| | | this.dialogVisible = true |
| | | }, |
| | | save() { |
| | | this.$refs.form.validate(valid => { |
| | | if (!valid) return |
| | | if (this.form.menuType === 2 && !this.form.permissions) { |
| | | this.$message.error('æé®æéæ è¯ä¸è½ä¸ºç©º') |
| | | return |
| | | } |
| | | const payload = { ...this.form } |
| | | if (payload.menuType === 2) { |
| | | payload.url = null |
| | | payload.icon = null |
| | | } |
| | | delete payload.children |
| | | this.saving = true |
| | | const request = payload.id ? api.put('/system/menu/' + payload.id, payload) : api.post('/system/menu', payload) |
| | | request.then(() => { |
| | | this.$message.success('ä¿åæå') |
| | | this.dialogVisible = false |
| | | this.load() |
| | | }).finally(() => { this.saving = false }) |
| | | }) |
| | | }, |
| | | remove(row) { |
| | | this.$confirm(`ç¡®å®å é¤â${row.name}âåï¼`, 'å é¤ç¡®è®¤', { type: 'warning' }) |
| | | .then(() => api.delete('/system/menu/' + row.id)) |
| | | .then(() => { this.$message.success('å 餿å'); this.load() }) |
| | | .catch(() => {}) |
| | | }, |
| | | openButtons(row) { |
| | | this.buttonMenu = row |
| | | api.get('/system/menu/buttons', { params: { menuId: row.id } }).then(res => { |
| | | const data = res.data || {} |
| | | this.buttonAvailable = data.available || [] |
| | | this.buttonSelected = data.selected || [] |
| | | this.buttonDialogVisible = true |
| | | }) |
| | | }, |
| | | saveButtons() { |
| | | this.buttonSaving = true |
| | | api.post('/system/menu/buttons', { menuId: this.buttonMenu.id, codes: this.buttonSelected }).then(() => { |
| | | this.$message.success('页颿é®å·²åæ¥') |
| | | this.buttonDialogVisible = false |
| | | this.load() |
| | | }).finally(() => { this.buttonSaving = false }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped> |
| | | .danger-link { color: #d9534f; } |
| | | .icon-cell { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; } |
| | | .muted { color: #a3afbc; } |
| | | .dialog-tip { margin: 0 0 12px; color: #738296; font-size: 13px; } |
| | | .button-list { display: flex; flex-direction: column; gap: 2px; } |
| | | .button-item { margin-right: 0; } |
| | | .button-name { color: #31465d; } |
| | | .button-code { margin-left: 8px; color: #93a2b1; font-size: 12px; } |
| | | .empty-tip { padding: 30px 0; color: #93a2b1; text-align: center; } |
| | | </style> |
| | |
| | | <div class="permission-grid"> |
| | | <section class="permission-panel"> |
| | | <div class="panel-head"><h3>ç§æ·èåææ</h3><el-checkbox v-model="tenantCheckAll" @change="checkAll('tenant')">å
¨é</el-checkbox></div> |
| | | <el-tree ref="tenantTree" :data="menus" node-key="id" show-checkbox check-strictly default-expand-all :props="{ label: 'name', children: 'children' }"></el-tree> |
| | | <el-tree ref="tenantTree" :data="menus" node-key="id" show-checkbox default-expand-all :render-content="renderContent" :props="{ label: 'name', children: 'children' }" @check="syncCheckAllState('tenant')"></el-tree> |
| | | </section> |
| | | <section class="permission-panel"> |
| | | <div class="panel-head"><h3>è§è²èåææ</h3><el-checkbox v-model="roleCheckAll" @change="checkAll('role')">å
¨é</el-checkbox></div> |
| | | <el-tree ref="roleTree" :data="menus" node-key="id" show-checkbox check-strictly default-expand-all :props="{ label: 'name', children: 'children' }"></el-tree> |
| | | <el-tree ref="roleTree" :data="menus" node-key="id" show-checkbox default-expand-all :render-content="renderContent" :props="{ label: 'name', children: 'children' }" @check="syncCheckAllState('role')"></el-tree> |
| | | </section> |
| | | <section class="permission-panel intersection-panel"> |
| | | <div class="panel-head"><h3>交éé¢è§</h3></div> |
| | |
| | | name: 'MenuPermission', |
| | | data() { return { menus: [], flatMenus: [], roles: [], tenants: [], tenantId: 1, roleId: null, saving: false, tenantCheckAll: false, roleCheckAll: false, selectedTenantIds: [], selectedRoleIds: [] } }, |
| | | computed: { |
| | | allMenuIds() { return this.flatMenus.map(item => item.id) }, |
| | | allMenuIds() { return this.flatMenus.map(item => String(item.id)) }, |
| | | parentMenuIds() { const ids = new Set(); this.flatMenus.forEach(item => ids.add(String(item.pid))); return ids }, |
| | | leafMenuIds() { return this.flatMenus.filter(item => !this.parentMenuIds.has(String(item.id))).map(item => String(item.id)) }, |
| | | effectiveCount() { return this.effectiveMenus.length }, |
| | | effectiveMenus() { return this.flatMenus.filter(item => this.selectedTenantIds.includes(item.id) && this.selectedRoleIds.includes(item.id)) } |
| | | effectiveMenus() { return this.flatMenus.filter(item => this.selectedTenantIds.map(String).includes(String(item.id)) && this.selectedRoleIds.map(String).includes(String(item.id))) } |
| | | }, |
| | | created() { this.loadBase() }, |
| | | methods: { |
| | | loadBase() { |
| | | Promise.all([api.get('/system/menu/nav'), api.get('/system/menu/list'), api.get('/system/roles'), api.get('/system/tenants')]).then(([tree, flat, roles, tenants]) => { |
| | | Promise.all([api.get('/system/menu/tree'), api.get('/system/menu/list'), api.get('/system/roles'), api.get('/system/tenants')]).then(([tree, flat, roles, tenants]) => { |
| | | this.menus = tree.data || []; this.flatMenus = flat.data || []; this.roles = roles.data || []; this.tenants = tenants.data || [] |
| | | if (!this.roleId && this.roles.length) this.roleId = this.roles[0].id |
| | | this.$nextTick(this.loadPermissions) |
| | |
| | | this.selectedTenantIds = data.tenantMenuIds || [] |
| | | this.selectedRoleIds = data.roleMenuIds || [] |
| | | this.$nextTick(() => { |
| | | this.$refs.tenantTree.setCheckedKeys(this.selectedTenantIds) |
| | | this.$refs.roleTree.setCheckedKeys(this.selectedRoleIds) |
| | | this.$refs.tenantTree.setCheckedKeys(this.leafKeys(this.selectedTenantIds)) |
| | | this.$refs.roleTree.setCheckedKeys(this.leafKeys(this.selectedRoleIds)) |
| | | this.syncCheckAllState('tenant'); this.syncCheckAllState('role') |
| | | }) |
| | | }) |
| | | }, |
| | | renderContent(h, { data }) { |
| | | const label = data.menuType === 2 ? data.name + ' [æé® ' + (data.permissions || '') + ']' : data.name |
| | | return h('span', { class: { 'perm-button-node': data.menuType === 2 } }, label) |
| | | }, |
| | | leafKeys(ids) { const leafIds = new Set(this.leafMenuIds); return (ids || []).map(String).filter(id => leafIds.has(id)) }, |
| | | checkedMenuIds(ref) { return [...new Set([...(ref.getCheckedKeys(false) || []), ...(ref.getHalfCheckedKeys() || [])].map(String))] }, |
| | | syncCheckAllState(target) { const ref = target === 'tenant' ? this.$refs.tenantTree : this.$refs.roleTree; if (!ref) return; const checked = this.checkedMenuIds(ref).length === this.allMenuIds.length; if (target === 'tenant') this.tenantCheckAll = checked; if (target === 'role') this.roleCheckAll = checked }, |
| | | checkAll(target) { |
| | | const checked = target === 'tenant' ? this.tenantCheckAll : this.roleCheckAll |
| | | const ref = target === 'tenant' ? this.$refs.tenantTree : this.$refs.roleTree |
| | | ref.setCheckedKeys(checked ? this.allMenuIds : []) |
| | | this.$nextTick(() => this.syncCheckAllState(target)) |
| | | }, |
| | | save() { |
| | | const tenantMenuIds = this.$refs.tenantTree.getCheckedKeys() |
| | | const roleMenuIds = this.$refs.roleTree.getCheckedKeys() |
| | | const tenantMenuIds = this.checkedMenuIds(this.$refs.tenantTree) |
| | | const roleMenuIds = this.checkedMenuIds(this.$refs.roleTree) |
| | | this.saving = true |
| | | api.post('/system/menu-permissions', { tenantId: this.tenantId, roleId: this.roleId, tenantMenuIds, roleMenuIds }).then(() => { |
| | | this.selectedTenantIds = tenantMenuIds; this.selectedRoleIds = roleMenuIds; this.$message.success('èåæéå·²ä¿å') |
| | |
| | | } |
| | | </script> |
| | | <style scoped> |
| | | ::v-deep .perm-button-node { color: #b8791f; } |
| | | |
| | | .permission-grid { display: grid; grid-template-columns: 1fr 1fr 320px; gap: 12px; } |
| | | .permission-panel { min-height: calc(100vh - 220px); padding: 14px; border: 1px solid #dce4ec; border-radius: 4px; background: #fff; overflow: auto; } |
| | | .panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } |
| New file |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div><h2>æä½æ¥å¿</h2><p>èªå¨è®°å½æ°å¢ãä¿®æ¹ãå é¤çåæä½ï¼ç»å½è¡ä¸ºåç¬è®°å
¥ç»å½æ¥å¿</p></div> |
| | | </div> |
| | | <div class="table-panel"> |
| | | <div class="toolbar"> |
| | | <el-input v-model="filters.keyword" clearable placeholder="è´¦æ·å / å§å / 请æ±å°å" style="width:230px" @keyup.enter.native="search"></el-input> |
| | | <el-select v-model="filters.module" clearable placeholder="模å" style="width:160px"> |
| | | <el-option v-for="item in modules" :key="item" :label="item" :value="item"></el-option> |
| | | </el-select> |
| | | <el-select v-model="filters.operationType" clearable placeholder="æä½ç±»å" style="width:150px"> |
| | | <el-option v-for="item in operationTypes" :key="item" :label="item" :value="item"></el-option> |
| | | </el-select> |
| | | <el-date-picker |
| | | v-model="filters.dates" |
| | | type="daterange" |
| | | value-format="yyyy-MM-dd" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | style="width:260px" |
| | | ></el-date-picker> |
| | | <el-button type="primary" icon="el-icon-search" v-permission="'system:operation-logs:query'" @click="search">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh-left" @click="reset">éç½®</el-button> |
| | | </div> |
| | | <el-table v-loading="loading" :data="rows" border stripe height="calc(100vh - 250px)"> |
| | | <el-table-column prop="createTime" label="æä½æ¶é´" width="180"></el-table-column> |
| | | <el-table-column prop="userName" label="è´¦æ·å" width="130"></el-table-column> |
| | | <el-table-column prop="nickName" label="å§å" width="120"></el-table-column> |
| | | <el-table-column prop="module" label="模å" width="130"></el-table-column> |
| | | <el-table-column prop="operationType" label="æä½ç±»å" width="110" align="center"></el-table-column> |
| | | <el-table-column prop="targetType" label="ç®æ ç±»å" width="120"></el-table-column> |
| | | <el-table-column prop="targetId" label="ç®æ ID" width="180" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="ç»æ" width="80" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag size="mini" :type="row.result === 'æå' ? 'success' : 'danger'">{{ row.result }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="requestMethod" label="æ¹å¼" width="80" align="center"></el-table-column> |
| | | <el-table-column prop="requestUri" label="请æ±å°å" min-width="240" show-overflow-tooltip></el-table-column> |
| | | </el-table> |
| | | <el-pagination |
| | | class="pager" |
| | | background |
| | | layout="total, sizes, prev, pager, next" |
| | | :total="total" |
| | | :current-page="current" |
| | | :page-size="size" |
| | | :page-sizes="[20, 50, 100]" |
| | | @current-change="handleCurrentChange" |
| | | @size-change="handleSizeChange" |
| | | ></el-pagination> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import api from '@/api' |
| | | export default { |
| | | name: 'OperationLogManage', |
| | | data() { |
| | | return { |
| | | rows: [], |
| | | total: 0, |
| | | current: 1, |
| | | size: 20, |
| | | loading: false, |
| | | modules: ['æ
é管ç', 'å®å·¥ç®¡ç', 'å®å·¥éªæ¶', 'ç»è®¡æ¥è¡¨', 'ç¨æ·ç®¡ç', 'è§è²ç®¡ç', 'èå管ç', 'æ¿ä¿®åä½ç®¡ç', 'ç³»ç»ç®¡ç', 'å
¶ä»'], |
| | | operationTypes: ['æ°å¢', 'ä¿®æ¹', 'å é¤', 'ææ', '审æ¹', 'æ´¾å', 'é项', 'æäº¤', 'ç³è¯·', 'æå°', 'è¯çº§', 'èåæé®é
ç½®'], |
| | | filters: { keyword: '', module: '', operationType: '', dates: [] } |
| | | } |
| | | }, |
| | | created() { this.load() }, |
| | | methods: { |
| | | load() { |
| | | this.loading = true |
| | | const dates = this.filters.dates || [] |
| | | api.get('/system/operation-logs', { |
| | | params: { |
| | | keyword: this.filters.keyword || undefined, |
| | | module: this.filters.module || undefined, |
| | | operationType: this.filters.operationType || undefined, |
| | | startDate: dates[0] || undefined, |
| | | endDate: dates[1] || undefined, |
| | | current: this.current, |
| | | size: this.size |
| | | } |
| | | }).then(res => { |
| | | const data = res.data || {} |
| | | this.rows = data.records || [] |
| | | this.total = data.total || 0 |
| | | }).finally(() => { this.loading = false }) |
| | | }, |
| | | search() { this.current = 1; this.load() }, |
| | | reset() { this.filters = { keyword: '', module: '', operationType: '', dates: [] }; this.search() }, |
| | | handleCurrentChange(page) { this.current = page; this.load() }, |
| | | handleSizeChange(size) { this.size = size; this.current = 1; this.load() } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped> |
| | | .toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; } |
| | | .pager { margin-top: 12px; text-align: right; } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>ç»ç»æºæ</h2> |
| | | <p>é¨é¨æ è¡¨æ¥æºäº SYS_DEPTï¼DEPT_TYPE 为 cj çé¨é¨å³æ¿ä¿®åä½</p> |
| | | </div> |
| | | <el-button icon="el-icon-refresh" @click="load">å·æ°</el-button> |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | | <el-input v-model="keyword" placeholder="é¨é¨åç§° / é¨é¨ç¼ç / è´è´£äºº" clearable style="width:280px"></el-input> |
| | | <el-select v-model="deptType" clearable placeholder="é¨é¨ç±»å" style="width:160px"> |
| | | <el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option> |
| | | </el-select> |
| | | <el-button type="primary" icon="el-icon-search" @click="load">æ¥è¯¢</el-button> |
| | | <div class="spacer"></div> |
| | | <span class="summary">å
± {{ rows.length }} 个é¨é¨ï¼å
¶ä¸æ¿ä¿®åä½ {{ contractorCount }} 个</span> |
| | | </div> |
| | | |
| | | <div class="table-panel"> |
| | | <el-table |
| | | v-loading="loading" |
| | | :data="filteredTree" |
| | | row-key="id" |
| | | border |
| | | stripe |
| | | default-expand-all |
| | | height="calc(100vh - 245px)" |
| | | :tree-props="{ children: 'children' }" |
| | | > |
| | | <el-table-column prop="name" label="é¨é¨åç§°" min-width="260"></el-table-column> |
| | | <el-table-column prop="deptCode" label="é¨é¨ç¼ç " width="150"></el-table-column> |
| | | <el-table-column label="é¨é¨ç±»å" width="130" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag v-if="row.deptType === 'cj'" size="mini" type="success">æ¿ä¿®åä½</el-tag> |
| | | <el-tag v-else size="mini" type="info">{{ row.deptType || '-' }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="leader" label="è´è´£äºº" width="120"></el-table-column> |
| | | <el-table-column prop="phone" label="èç³»çµè¯" width="150"></el-table-column> |
| | | <el-table-column prop="orderNum" label="æåº" width="80" align="center"></el-table-column> |
| | | <el-table-column label="ç¶æ" width="90" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag size="mini" :type="row.status === '1' ? 'info' : 'success'">{{ row.status === '1' ? 'åç¨' : 'æ£å¸¸' }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import api from '@/api' |
| | | export default { |
| | | name: 'OrganizationManage', |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | rows: [], |
| | | keyword: '', |
| | | deptType: '' |
| | | } |
| | | }, |
| | | computed: { |
| | | typeOptions() { |
| | | const values = Array.from(new Set(this.rows.map(item => item.deptType).filter(Boolean))).sort() |
| | | return values.map(value => ({ value, label: value === 'cj' ? 'æ¿ä¿®åä½' : value })) |
| | | }, |
| | | contractorCount() { |
| | | return this.rows.filter(item => item.deptType === 'cj').length |
| | | }, |
| | | tree() { |
| | | return this.buildTree(this.rows) |
| | | }, |
| | | filteredTree() { |
| | | const keyword = this.keyword.trim().toLowerCase() |
| | | const type = this.deptType |
| | | const hit = node => { |
| | | const textOk = !keyword || [node.name, node.deptCode, node.leader] |
| | | .some(value => String(value || '').toLowerCase().includes(keyword)) |
| | | const typeOk = !type || node.deptType === type |
| | | return textOk && typeOk |
| | | } |
| | | const walk = nodes => nodes.reduce((result, node) => { |
| | | const children = walk(node.children || []) |
| | | if (hit(node) || children.length) { |
| | | result.push({ ...node, children }) |
| | | } |
| | | return result |
| | | }, []) |
| | | return walk(this.tree) |
| | | } |
| | | }, |
| | | created() { this.load() }, |
| | | methods: { |
| | | load() { |
| | | this.loading = true |
| | | api.get('/system/depts').then(res => { |
| | | this.rows = res.data || [] |
| | | }).finally(() => { this.loading = false }) |
| | | }, |
| | | buildTree(rows) { |
| | | const nodes = rows.map(row => ({ ...row, children: [] })) |
| | | const map = new Map(nodes.map(node => [String(node.id), node])) |
| | | const roots = [] |
| | | nodes.forEach(node => { |
| | | const parentId = node.parentId == null ? null : String(node.parentId) |
| | | if (parentId && parentId !== String(node.id) && map.has(parentId)) { |
| | | map.get(parentId).children.push(node) |
| | | } else { |
| | | roots.push(node) |
| | | } |
| | | }) |
| | | const sortNodes = list => { |
| | | list.sort((left, right) => (Number(left.orderNum) || 0) - (Number(right.orderNum) || 0) |
| | | || String(left.name || '').localeCompare(String(right.name || ''), 'zh')) |
| | | list.forEach(item => sortNodes(item.children || [])) |
| | | } |
| | | sortNodes(roots) |
| | | return roots |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped> |
| | | .summary { color: #6d7f91; font-size: 13px; } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div><h2>è§è²ç®¡ç</h2><p>第ä¸çæä¸å¯ç¨è§è²æ§å¶ï¼é¡µé¢åæ°æ®ç»æä¸ºåç»æéå¯ç¨é¢ç</p></div> |
| | | <el-button type="primary" icon="el-icon-plus" @click="openCreate">æ°å¢è§è²</el-button> |
| | | <div><h2>è§è²ç®¡ç</h2><p>ç»´æ¤è§è²ä¿¡æ¯ï¼å¹¶å¯æè§è²æ¹é设置æå±ç¨æ·</p></div> |
| | | <el-button type="primary" icon="el-icon-plus" v-permission="'system:roles:add'" @click="openCreate">æ°å¢è§è²</el-button> |
| | | </div> |
| | | <div class="table-panel"> |
| | | <el-table :data="rows" border stripe height="calc(100vh - 190px)"> |
| | |
| | | <el-table-column prop="name" label="è§è²åç§°" min-width="180"></el-table-column> |
| | | <el-table-column prop="code" label="è§è²ç¼ç " width="170"></el-table-column> |
| | | <el-table-column prop="roleType" label="è§è²ç±»å" width="170"></el-table-column> |
| | | <el-table-column prop="remark" label="夿³¨" min-width="260" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="æä½" width="130" fixed="right"><template slot-scope="{ row }"><el-button type="text" @click="openEdit(row)">ç¼è¾</el-button><el-button type="text" class="danger-link" @click="remove(row)">å é¤</el-button></template></el-table-column> |
| | | <el-table-column prop="remark" label="夿³¨" min-width="220" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="æä½" width="220" fixed="right"> |
| | | <template slot-scope="{ row }"> |
| | | <el-button type="text" v-permission="'system:roles:assign'" @click="openUserSetting(row)">ç¨æ·è®¾ç½®</el-button> |
| | | <el-button type="text" v-permission="'system:roles:edit'" @click="openEdit(row)">ç¼è¾</el-button> |
| | | <el-button type="text" class="danger-link" v-permission="'system:roles:delete'" @click="remove(row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-dialog :title="form.id ? 'ç¼è¾è§è²' : 'æ°å¢è§è²'" :visible.sync="dialogVisible" width="560px" append-to-body> |
| | | <el-form ref="form" :model="form" :rules="rules" label-width="95px"> |
| | | <el-form-item label="è§è²åç§°" prop="name"><el-input v-model="form.name"></el-input></el-form-item> |
| | | <el-form-item label="è§è²ç¼ç " prop="code"><el-input v-model="form.code"></el-input></el-form-item> |
| | | <el-form-item label="è§è²ç±»å"><el-select v-model="form.roleType" style="width:100%"><el-option label="ä¸å¡è§è²" value="BUSINESS"></el-option><el-option label="ç³»ç»ç®¡çå" value="SYSTEM_ADMIN"></el-option><el-option label="å®å
¨ä¿å¯ç®¡çå" value="SECURITY_ADMIN"></el-option><el-option label="å®å
¨å®¡è®¡ç®¡çå" value="AUDIT_ADMIN"></el-option></el-select></el-form-item> |
| | | <el-form-item label="è§è²ç±»å"> |
| | | <el-select v-model="form.roleType" style="width:100%"> |
| | | <el-option label="ä¸å¡è§è²" value="BUSINESS"></el-option> |
| | | <el-option label="ç³»ç»ç®¡çå" value="SYSTEM_ADMIN"></el-option> |
| | | <el-option label="å®å
¨ä¿å¯ç®¡çå" value="SECURITY_ADMIN"></el-option> |
| | | <el-option label="å®å
¨å®¡è®¡ç®¡çå" value="AUDIT_ADMIN"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æåº"><el-input-number v-model="form.sortNo" :min="1" style="width:160px"></el-input-number></el-form-item> |
| | | <el-form-item label="夿³¨"><el-input v-model="form.remark" type="textarea" :rows="3"></el-input></el-form-item> |
| | | </el-form> |
| | | <template slot="footer"><el-button @click="dialogVisible = false">åæ¶</el-button><el-button type="primary" :loading="saving" @click="save">ä¿å</el-button></template> |
| | | </el-dialog> |
| | | |
| | | <el-dialog title="è§è²ç¨æ·è®¾ç½®" :visible.sync="userDialogVisible" width="820px" append-to-body :close-on-click-modal="false"> |
| | | <div class="user-setting-head"> |
| | | <div> |
| | | <span class="role-label">å½åè§è²</span> |
| | | <strong>{{ currentRole.name }}</strong> |
| | | <span class="role-code">{{ currentRole.code }}</span> |
| | | </div> |
| | | <span class="selected-total">已鿩 {{ selectedUserIds.length }} 人</span> |
| | | </div> |
| | | <div class="user-setting-toolbar"> |
| | | <el-radio-group v-model="userScope" size="small"> |
| | | <el-radio-button label="all">å
¨é¨</el-radio-button> |
| | | <el-radio-button label="assigned">已设置</el-radio-button> |
| | | </el-radio-group> |
| | | <el-input v-model="userKeyword" clearable prefix-icon="el-icon-search" placeholder="ç¨æ·å / è´¦æ·å / é¨é¨" style="width: 280px"></el-input> |
| | | </div> |
| | | <el-table |
| | | ref="roleUserTable" |
| | | v-loading="userLoading" |
| | | :data="filteredUsers" |
| | | row-key="userId" |
| | | height="430" |
| | | border |
| | | stripe |
| | | @selection-change="handleUserSelectionChange" |
| | | > |
| | | <el-table-column type="selection" reserve-selection width="48" align="center"></el-table-column> |
| | | <el-table-column prop="nickName" label="ç¨æ·å" min-width="120"></el-table-column> |
| | | <el-table-column prop="userName" label="è´¦æ·å" min-width="140"></el-table-column> |
| | | <el-table-column prop="deptName" label="æå±é¨é¨" min-width="180" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="ç¶æ" width="80" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag size="mini" :type="row.status === '1' ? 'info' : 'success'">{{ row.status === '1' ? 'åç¨' : 'æ£å¸¸' }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <template slot="footer"> |
| | | <el-button @click="userDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" :loading="userSaving" v-permission="'system:roles:assign'" @click="saveRoleUsers">ä¿å</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | |
| | | import api from '@/api' |
| | | export default { |
| | | name: 'RoleManage', |
| | | data() { return { rows: [], dialogVisible: false, saving: false, form: {}, rules: { name: [{ required: true, message: '请è¾å
¥è§è²åç§°', trigger: 'blur' }], code: [{ required: true, message: '请è¾å
¥è§è²ç¼ç ', trigger: 'blur' }] } } }, |
| | | data() { |
| | | return { |
| | | rows: [], |
| | | dialogVisible: false, |
| | | saving: false, |
| | | userDialogVisible: false, |
| | | userLoading: false, |
| | | userSaving: false, |
| | | userKeyword: '', |
| | | userScope: 'all', |
| | | roleUsers: [], |
| | | selectedUserIds: [], |
| | | currentRole: {}, |
| | | form: {}, |
| | | rules: { |
| | | name: [{ required: true, message: '请è¾å
¥è§è²åç§°', trigger: 'blur' }], |
| | | code: [{ required: true, message: '请è¾å
¥è§è²ç¼ç ', trigger: 'blur' }] |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | filteredUsers() { |
| | | const assigned = new Set(this.selectedUserIds.map(String)) |
| | | const users = this.userScope === 'assigned' |
| | | ? this.roleUsers.filter(item => assigned.has(String(item.userId))) |
| | | : this.roleUsers |
| | | const keyword = this.userKeyword.trim().toLowerCase() |
| | | if (!keyword) return users |
| | | return users.filter(item => [item.nickName, item.userName, item.deptName] |
| | | .some(value => String(value || '').toLowerCase().includes(keyword))) |
| | | } |
| | | }, |
| | | created() { this.load() }, |
| | | methods: { |
| | | load() { api.get('/system/roles').then(res => { this.rows = res.data || [] }) }, |
| | | openCreate() { this.form = { name: '', code: '', roleType: 'BUSINESS', sortNo: this.rows.length + 1, remark: '' }; this.dialogVisible = true }, |
| | | openEdit(row) { this.form = { ...row }; this.dialogVisible = true }, |
| | | save() { this.$refs.form.validate(valid => { if (!valid) return; this.saving = true; const request = this.form.id ? api.put('/system/roles/' + this.form.id, this.form) : api.post('/system/roles', this.form); request.then(() => { this.$message.success('ä¿åæå'); this.dialogVisible = false; this.load() }).finally(() => { this.saving = false }) }) }, |
| | | remove(row) { this.$confirm(`ç¡®å®å é¤è§è²â${row.name}âåï¼`, 'å é¤ç¡®è®¤', { type: 'warning' }).then(() => api.delete('/system/roles/' + row.id)).then(() => { this.$message.success('å 餿å'); this.load() }).catch(() => {}) } |
| | | load() { |
| | | api.get('/system/roles').then(res => { this.rows = res.data || [] }) |
| | | }, |
| | | openCreate() { |
| | | this.form = { name: '', code: '', roleType: 'BUSINESS', sortNo: this.rows.length + 1, remark: '' } |
| | | this.dialogVisible = true |
| | | }, |
| | | openEdit(row) { |
| | | this.form = { ...row } |
| | | this.dialogVisible = true |
| | | }, |
| | | save() { |
| | | this.$refs.form.validate(valid => { |
| | | if (!valid) return |
| | | this.saving = true |
| | | const request = this.form.id ? api.put('/system/roles/' + this.form.id, this.form) : api.post('/system/roles', this.form) |
| | | request.then(() => { |
| | | this.$message.success('ä¿åæå') |
| | | this.dialogVisible = false |
| | | this.load() |
| | | }).finally(() => { this.saving = false }) |
| | | }) |
| | | }, |
| | | remove(row) { |
| | | this.$confirm(`ç¡®å®å é¤è§è²â${row.name}âåï¼`, 'å é¤ç¡®è®¤', { type: 'warning' }) |
| | | .then(() => api.delete('/system/roles/' + row.id)) |
| | | .then(() => { this.$message.success('å 餿å'); this.load() }) |
| | | .catch(() => {}) |
| | | }, |
| | | openUserSetting(row) { |
| | | this.currentRole = { ...row } |
| | | this.userKeyword = '' |
| | | this.userScope = 'all' |
| | | this.selectedUserIds = [] |
| | | this.roleUsers = [] |
| | | this.userDialogVisible = true |
| | | this.userLoading = true |
| | | api.get('/system/roles/' + row.id + '/users').then(res => { |
| | | const data = res.data || {} |
| | | this.roleUsers = data.records || [] |
| | | this.selectedUserIds = (data.userIds || []).map(String) |
| | | this.$nextTick(this.restoreUserSelection) |
| | | }).finally(() => { this.userLoading = false }) |
| | | }, |
| | | restoreUserSelection() { |
| | | const table = this.$refs.roleUserTable |
| | | if (!table) return |
| | | table.clearSelection() |
| | | const selected = new Set(this.selectedUserIds) |
| | | this.roleUsers.forEach(row => { |
| | | if (selected.has(String(row.userId))) table.toggleRowSelection(row, true) |
| | | }) |
| | | this.$nextTick(() => { |
| | | table.clearSelection() |
| | | this.roleUsers.forEach(row => { |
| | | if (selected.has(String(row.userId))) table.toggleRowSelection(row, true) |
| | | }) |
| | | this.selectedUserIds = Array.from(selected) |
| | | }) |
| | | }, |
| | | handleUserSelectionChange(selection) { |
| | | this.selectedUserIds = (selection || []).map(item => String(item.userId)) |
| | | }, |
| | | saveRoleUsers() { |
| | | const userIds = this.selectedUserIds.slice() |
| | | const message = userIds.length |
| | | ? `ç¡®å®å°â${this.currentRole.name}âè§è²è®¾ç½®ä¸ºéä¸ç ${userIds.length} ä¸ªç¨æ·åï¼` |
| | | : `æªéæ©ç¨æ·ï¼å°æ¸
空â${this.currentRole.name}âè§è²çå
¨é¨ç¨æ·ï¼æ¯å¦ç»§ç»ï¼` |
| | | this.$confirm(message, 'è§è²ç¨æ·è®¾ç½®', { type: 'warning' }).then(() => { |
| | | this.userSaving = true |
| | | api.put('/system/roles/' + this.currentRole.id + '/users', { userIds }).then(() => { |
| | | this.$message.success('è§è²ç¨æ·è®¾ç½®å·²ä¿å') |
| | | this.userDialogVisible = false |
| | | }).finally(() => { this.userSaving = false }) |
| | | }).catch(() => {}) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped>.danger-link { color: #d9534f; }</style> |
| | | <style scoped> |
| | | .danger-link { color: #d9534f; } |
| | | .user-setting-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; color: #31465d; } |
| | | .role-label { margin-right: 10px; color: #7b8b9c; font-size: 13px; } |
| | | .role-code { margin-left: 8px; color: #93a2b1; font-size: 12px; } |
| | | .selected-total { color: #2f658f; font-size: 13px; } |
| | | .user-setting-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; } |
| | | </style> |
| | |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>Téä¸ä¸é
ç½®</h2> |
| | | <h2>rk_Téä¸ä¸é
ç½®</h2> |
| | | <p>åç±»åå¼AãBï¼ç¨äºèªå¨çææ
éç¼å·</p> |
| | | </div> |
| | | <el-button type="primary" icon="el-icon-plus" @click="openCreate">æ°å¢Téä¸ä¸</el-button> |
| | | <el-button type="primary" icon="el-icon-plus" @click="openCreate">æ°å¢rk_Téä¸ä¸</el-button> |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-dialog :title="form.id ? 'ç¼è¾Téä¸ä¸' : 'æ°å¢Téä¸ä¸'" :visible.sync="dialogVisible" width="520px" append-to-body> |
| | | <el-dialog :title="form.id ? 'ç¼è¾rk_Téä¸ä¸' : 'æ°å¢rk_Téä¸ä¸'" :visible.sync="dialogVisible" width="520px" append-to-body> |
| | | <el-form ref="form" :model="form" :rules="rules" label-width="80px"> |
| | | <el-form-item label="åç§°" prop="name"><el-input v-model="form.name" placeholder="请è¾å
¥Téä¸ä¸åç§°"></el-input></el-form-item> |
| | | <el-form-item label="åç§°" prop="name"><el-input v-model="form.name" placeholder="请è¾å
¥rk_Téä¸ä¸åç§°"></el-input></el-form-item> |
| | | <el-form-item label="åç±»" prop="category"> |
| | | <el-radio-group v-model="form.category"> |
| | | <el-radio-button label="A">A</el-radio-button> |
| | |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>{{ isProduct ? '产åç»ææ 管ç' : '设å¤åºç¡åºç®¡ç' }}</h2> |
| | | <p>{{ isProduct ? 'åå· â Xå· â ä¸çº§ç³»ç» â äºçº§ç³»ç» â 设å¤' : 'ä¸çº§ç³»ç» â äºçº§ç³»ç» â 设å¤' }}</p> |
| | | <p>{{ isProduct ? 'åå· â rk_Xå· â ä¸çº§ç³»ç» â äºçº§ç³»ç» â 设å¤' : 'ä¸çº§ç³»ç» â äºçº§ç³»ç» â 设å¤' }}</p> |
| | | </div> |
| | | <div> |
| | | <el-button icon="el-icon-plus" @click="addRoot">æ°å¢ä¸çº§èç¹</el-button> |
| | |
| | | ref="tree" |
| | | :data="tree" |
| | | node-key="id" |
| | | default-expand-all |
| | | v-loading="loading" |
| | | :lazy="isProduct" |
| | | :load="loadNode" |
| | | :default-expand-all="!isProduct" |
| | | highlight-current |
| | | :filter-node-method="filterNode" |
| | | :props="{ label: 'name', children: 'children' }" |
| | |
| | | <span slot-scope="{ data }" class="tree-node"> |
| | | <span class="level-badge">{{ levelText(data.nodeLevel) }}</span> |
| | | <span>{{ data.name }}</span> |
| | | <small>{{ data.code }}</small> |
| | | <small v-if="!isProduct && data.code">{{ data.code }}</small> |
| | | </span> |
| | | </el-tree> |
| | | </section> |
| | |
| | | <el-form :model="form" label-width="100px"> |
| | | <el-form-item label="èç¹å±çº§"><el-tag type="info">{{ levelText(form.nodeLevel) }}</el-tag></el-form-item> |
| | | <el-form-item label="åç§°"><el-input v-model="form.name"></el-input></el-form-item> |
| | | <el-form-item label="ç¼ç "><el-input v-model="form.code"></el-input></el-form-item> |
| | | <el-form-item v-if="!isProduct" label="ç¼ç "><el-input v-model="form.code"></el-input></el-form-item> |
| | | <el-form-item v-if="isProduct" label="èç¹ç±»å"> |
| | | <el-select v-model="form.nodeType" :disabled="form.nodeLevel < 5" style="width:100%"> |
| | | <el-option label="åå·" value="MODEL"></el-option> |
| | | <el-option label="Xå·" value="X_NO"></el-option> |
| | | <el-option label="rk_Xå·" value="X_NO"></el-option> |
| | | <el-option label="ä¸çº§ç³»ç»" value="SYSTEM1"></el-option> |
| | | <el-option label="äºçº§ç³»ç»" value="SYSTEM2"></el-option> |
| | | <el-option label="设å¤" value="DEVICE"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item v-if="isProduct && form.nodeLevel === 5" label="åºç¡è®¾å¤"> |
| | | <el-select v-model="form.baseEquipmentId" filterable placeholder="å
³è设å¤åºç¡åºè®¾å¤" style="width:100%"> |
| | | <el-option v-for="item in baseDevices" :key="item.id" :label="item.path + ' / ' + item.name" :value="item.id"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æåº"><el-input-number v-model="form.sortNo" :min="1" style="width:180px"></el-input-number></el-form-item> |
| | |
| | | treeType: { type: String, required: true } |
| | | }, |
| | | data() { |
| | | return { tree: [], keyword: '', form: {}, saving: false, baseDevices: [] } |
| | | return { tree: [], keyword: '', form: {}, saving: false, loading: false } |
| | | }, |
| | | computed: { |
| | | isProduct() { return this.treeType === 'product' }, |
| | | maxLevel() { return this.isProduct ? 5 : 3 }, |
| | | levelNames() { return this.isProduct ? ['åå·', 'Xå·', 'ä¸çº§ç³»ç»', 'äºçº§ç³»ç»', '设å¤'] : ['ä¸çº§ç³»ç»', 'äºçº§ç³»ç»', '设å¤'] } |
| | | levelNames() { return this.isProduct ? ['åå·', 'rk_Xå·', 'ä¸çº§ç³»ç»', 'äºçº§ç³»ç»', '设å¤'] : ['ä¸çº§ç³»ç»', 'äºçº§ç³»ç»', '设å¤'] } |
| | | }, |
| | | watch: { |
| | | keyword(value) { this.$refs.tree && this.$refs.tree.filter(value) }, |
| | |
| | | created() { this.load() }, |
| | | methods: { |
| | | load() { |
| | | api.get('/system/tree', { params: { type: this.treeType } }).then(res => { this.tree = res.data || [] }) |
| | | this.loading = true |
| | | const request = this.isProduct |
| | | ? api.get('/system/tree/product/roots') |
| | | : api.get('/system/tree', { params: { type: this.treeType } }) |
| | | request.then(res => { this.tree = res.data || [] }).finally(() => { this.loading = false }) |
| | | }, |
| | | loadNode(node, resolve) { |
| | | if (!this.isProduct || !node || !node.data || !node.data.id) { |
| | | resolve([]) |
| | | return |
| | | } |
| | | api.get('/system/tree/product/children', { params: { parentId: node.data.id } }) |
| | | .then(res => resolve(res.data || [])) |
| | | .catch(() => resolve([])) |
| | | }, |
| | | levelText(level) { return this.levelNames[(level || 1) - 1] || ('第' + level + '级') }, |
| | | filterNode(value, data) { |
| | |
| | | }, |
| | | selectNode(node) { |
| | | this.form = { ...node } |
| | | if (this.isProduct && node.nodeLevel === 5) this.loadBaseDevices() |
| | | }, |
| | | nodeTypeForLevel(level) { |
| | | return this.isProduct ? ['MODEL', 'X_NO', 'SYSTEM1', 'SYSTEM2', 'DEVICE'][level - 1] : 'BASE_' + level |
| | | }, |
| | | addRoot() { |
| | | this.form = { parentId: 0, nodeLevel: 1, nodeType: this.nodeTypeForLevel(1), name: '', code: '', sortNo: 1, remark: '' } |
| | | if (this.isProduct) this.form.baseEquipmentId = null |
| | | }, |
| | | addChild() { |
| | | const level = (this.form.nodeLevel || 1) + 1 |
| | | this.form = { parentId: this.form.id, nodeLevel: level, nodeType: this.nodeTypeForLevel(level), name: '', code: '', sortNo: 1, remark: '', baseEquipmentId: null } |
| | | if (this.isProduct && level === 5) this.loadBaseDevices() |
| | | }, |
| | | loadBaseDevices() { |
| | | api.get('/system/tree', { params: { type: 'base' } }).then(res => { this.baseDevices = this.flattenBase(res.data || [], []) }) |
| | | }, |
| | | flattenBase(nodes, parents) { |
| | | const result = [] |
| | | nodes.forEach(node => { |
| | | const path = parents.concat(node.name) |
| | | if (node.nodeLevel === 3) result.push({ ...node, path: path.slice(0, -1).join(' / ') }) |
| | | if (node.children) result.push(...this.flattenBase(node.children, path)) |
| | | }) |
| | | return result |
| | | this.form = { parentId: this.form.id, nodeLevel: level, nodeType: this.nodeTypeForLevel(level), name: '', code: '', sortNo: 1, remark: '' } |
| | | }, |
| | | save() { |
| | | if (!this.form.name) return this.$message.warning('请è¾å
¥èç¹åç§°') |
| New file |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div><h2>ç¨æ·ç®¡ç</h2><p>左侧æ¥è¯¢ç¨æ·ï¼å³ä¾§è®¾ç½®è¯¥ç¨æ·æ¥æçè§è²ï¼ä¸ä¸ªç¨æ·å¯è®¾ç½®å¤ä¸ªè§è²</p></div> |
| | | </div> |
| | | <div class="user-layout"> |
| | | <section class="user-left"> |
| | | <div class="toolbar"> |
| | | <el-button type="primary" plain icon="el-icon-s-check" :disabled="!selectedRows.length" v-permission="'system:users:assign'" @click="openBatchDialog">æ¹é设置è§è²</el-button> |
| | | <span v-if="selectedRows.length" class="selected-count">å·²é {{ selectedRows.length }} 人</span> |
| | | <el-input v-model="filters.keyword" clearable placeholder="ç¨æ·å / è´¦æ·å" style="width:190px" @keyup.enter.native="search"></el-input> |
| | | <el-select v-model="filters.deptId" clearable filterable placeholder="æå±é¨é¨" style="width:200px"> |
| | | <el-option v-for="item in depts" :key="item.id" :label="item.name" :value="item.id"></el-option> |
| | | </el-select> |
| | | <el-select v-model="filters.roleId" clearable placeholder="è§è²" style="width:170px"> |
| | | <el-option v-for="item in roles" :key="item.id" :label="item.name" :value="item.id"></el-option> |
| | | </el-select> |
| | | <el-button type="primary" icon="el-icon-search" v-permission="'system:users:query'" @click="search">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh-left" @click="reset">éç½®</el-button> |
| | | </div> |
| | | <el-table ref="userTable" row-key="userId" |
| | | v-loading="loading" |
| | | :data="rows" |
| | | border |
| | | stripe |
| | | highlight-current-row |
| | | height="calc(100vh - 250px)" |
| | | @selection-change="selectedRows = $event" |
| | | @current-change="selectUser" |
| | | > |
| | | <el-table-column type="selection" width="46" align="center"></el-table-column> |
| | | <el-table-column prop="nickName" label="ç¨æ·å" min-width="110"></el-table-column> |
| | | <el-table-column prop="userName" label="è´¦æ·å" min-width="140"></el-table-column> |
| | | <el-table-column prop="deptName" label="æå±é¨é¨" min-width="180" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="è§è²æé" min-width="220"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag v-for="role in row.roles" :key="role.roleId" size="mini" class="role-tag">{{ role.roleName }}</el-tag> |
| | | <span v-if="!row.roles || !row.roles.length" class="muted">æªåé
</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="phoneNumber" label="ææºå·" width="130"></el-table-column> |
| | | <el-table-column label="ç¶æ" width="80" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag size="mini" :type="isDisabled(row) ? 'info' : 'success'">{{ isDisabled(row) ? 'åç¨' : 'æ£å¸¸' }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <el-pagination |
| | | class="pager" |
| | | background |
| | | layout="total, sizes, prev, pager, next" |
| | | :total="total" |
| | | :current-page="current" |
| | | :page-size="size" |
| | | :page-sizes="[20, 50, 100]" |
| | | @current-change="handleCurrentChange" |
| | | @size-change="handleSizeChange" |
| | | ></el-pagination> |
| | | </section> |
| | | <section class="user-right"> |
| | | <div class="panel-head"> |
| | | <h3>è§è²ææ</h3> |
| | | <span v-if="selected" class="selected-user">{{ selected.nickName || selected.userName }}ï¼{{ selected.userName }}ï¼</span> |
| | | </div> |
| | | <div v-if="!selected" class="empty-tip">è¯·éæ©å·¦ä¾§ç¨æ·</div> |
| | | <template v-else> |
| | | <el-checkbox-group v-model="checkedRoleIds" class="role-list"> |
| | | <el-checkbox v-for="item in roles" :key="item.id" :label="item.id" class="role-item"> |
| | | <span class="role-name">{{ item.name }}</span> |
| | | <span class="role-code">{{ item.code }}</span> |
| | | </el-checkbox> |
| | | </el-checkbox-group> |
| | | <el-button |
| | | type="primary" |
| | | icon="el-icon-check" |
| | | class="save-button" |
| | | v-permission="'system:users:assign'" |
| | | :loading="saving" |
| | | @click="saveRoles" |
| | | >ä¿åææ</el-button> |
| | | </template> |
| | | </section> |
| | | </div> |
| | | <el-dialog title="æ¹é设置è§è²" :visible.sync="batchVisible" width="520px" append-to-body :close-on-click-modal="false"> |
| | | <div class="batch-summary">已鿩 {{ selectedRows.length }} ä¸ªç¨æ·</div> |
| | | <el-radio-group v-model="batchMode" class="batch-mode"> |
| | | <el-radio label="append">追å è§è²</el-radio> |
| | | <el-radio label="replace">è¦çè§è²</el-radio> |
| | | </el-radio-group> |
| | | <el-alert :title="batchMode === 'append' ? 'ä¿çæéç¨æ·åæè§è²ï¼å¹¶è¿½å å¾éè§è²' : '使ç¨å¾éè§è²è¦çæéç¨æ·çåæè§è²'" type="warning" :closable="false" show-icon></el-alert> |
| | | <el-checkbox-group v-model="batchRoleIds" class="batch-role-list"> |
| | | <el-checkbox v-for="item in roles" :key="item.id" :label="item.id" class="role-item"> |
| | | <span class="role-name">{{ item.name }}</span> |
| | | <span class="role-code">{{ item.code }}</span> |
| | | </el-checkbox> |
| | | </el-checkbox-group> |
| | | <span slot="footer"> |
| | | <el-button @click="batchVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" :loading="batchSaving" v-permission="'system:users:assign'" @click="saveBatchRoles">ä¿å</el-button> |
| | | </span> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import api from '@/api' |
| | | export default { |
| | | name: 'UserManage', |
| | | data() { |
| | | return { |
| | | rows: [], |
| | | total: 0, |
| | | current: 1, |
| | | size: 20, |
| | | loading: false, |
| | | saving: false, |
| | | batchSaving: false, |
| | | batchVisible: false, |
| | | batchRoleIds: [], |
| | | batchMode: 'append', |
| | | selectedRows: [], |
| | | depts: [], |
| | | roles: [], |
| | | selected: null, |
| | | checkedRoleIds: [], |
| | | filters: { keyword: '', deptId: null, roleId: null } |
| | | } |
| | | }, |
| | | created() { |
| | | this.loadRoles() |
| | | this.loadDepts() |
| | | this.load() |
| | | }, |
| | | methods: { |
| | | load() { |
| | | this.clearSelection() |
| | | this.loading = true |
| | | api.get('/system/users', { |
| | | params: { |
| | | keyword: this.filters.keyword || undefined, |
| | | deptId: this.filters.deptId || undefined, |
| | | roleId: this.filters.roleId || undefined, |
| | | current: this.current, |
| | | size: this.size |
| | | } |
| | | }).then(res => { |
| | | const data = res.data || {} |
| | | this.rows = data.records || [] |
| | | this.total = data.total || 0 |
| | | if (this.selected) { |
| | | const matched = this.rows.find(item => item.userId === this.selected.userId) |
| | | this.selected = matched || null |
| | | if (!matched) this.checkedRoleIds = [] |
| | | } |
| | | }).finally(() => { this.loading = false }) |
| | | }, |
| | | loadRoles() { |
| | | api.get('/system/roles').then(res => { this.roles = res.data || [] }) |
| | | }, |
| | | loadDepts() { |
| | | api.get('/system/depts').then(res => { this.depts = res.data || [] }) |
| | | }, |
| | | search() { |
| | | this.current = 1 |
| | | this.load() |
| | | }, |
| | | reset() { |
| | | this.filters = { keyword: '', deptId: null, roleId: null } |
| | | this.search() |
| | | }, |
| | | handleCurrentChange(page) { |
| | | this.current = page |
| | | this.load() |
| | | }, |
| | | handleSizeChange(size) { |
| | | this.size = size |
| | | this.current = 1 |
| | | this.load() |
| | | }, |
| | | clearSelection() { |
| | | this.selectedRows = [] |
| | | this.$nextTick(() => { if (this.$refs.userTable) this.$refs.userTable.clearSelection() }) |
| | | }, |
| | | openBatchDialog() { |
| | | if (!this.selectedRows.length) return |
| | | this.batchRoleIds = [] |
| | | this.batchMode = 'append' |
| | | this.batchVisible = true |
| | | }, |
| | | saveBatchRoles() { |
| | | if (!this.selectedRows.length) return |
| | | const userIds = this.selectedRows.map(item => item.userId) |
| | | const roleIds = this.batchRoleIds.slice() |
| | | if (this.batchMode === 'append' && !roleIds.length) { |
| | | this.$message.warning('è¯·éæ©è¦è¿½å çè§è²') |
| | | return |
| | | } |
| | | const message = this.batchMode === 'append' |
| | | ? 'å°å ' + userIds.length + ' ä¸ªç¨æ·è¿½å æéè§è²ï¼æ¯å¦ç»§ç»ï¼' |
| | | : (roleIds.length ? 'å°ä½¿ç¨æéè§è²è¦ç ' + userIds.length + ' ä¸ªç¨æ·çç°æè§è²ï¼æ¯å¦ç»§ç»ï¼' : 'æªéæ©è§è²ï¼å°æ¸
空 ' + userIds.length + ' ä¸ªç¨æ·çå
¨é¨è§è²ï¼æ¯å¦ç»§ç»ï¼') |
| | | this.$confirm(message, 'æ¹é设置è§è²', { type: 'warning' }).then(() => { |
| | | this.batchSaving = true |
| | | api.put('/system/users/roles/batch', { userIds, roleIds, mode: this.batchMode }).then(() => { |
| | | this.$message.success('æ¹éè§è²è®¾ç½®æå') |
| | | this.batchVisible = false |
| | | this.load() |
| | | if (this.selected) this.selectUser(this.selected) |
| | | }).finally(() => { this.batchSaving = false }) |
| | | }).catch(() => {}) |
| | | }, |
| | | selectUser(row) { |
| | | this.selected = row || null |
| | | this.checkedRoleIds = [] |
| | | if (!row) return |
| | | api.get('/system/users/' + row.userId + '/roles').then(res => { |
| | | this.checkedRoleIds = res.data || [] |
| | | }) |
| | | }, |
| | | saveRoles() { |
| | | if (!this.selected) return |
| | | this.saving = true |
| | | api.put('/system/users/' + this.selected.userId + '/roles', { roleIds: this.checkedRoleIds }).then(() => { |
| | | this.$message.success('è§è²ææå·²ä¿å') |
| | | this.load() |
| | | }).finally(() => { this.saving = false }) |
| | | }, |
| | | isDisabled(row) { |
| | | return row.status === '1' |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped> |
| | | .user-layout { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 12px; } |
| | | .user-left, .user-right { min-width: 0; padding: 14px; border: 1px solid #dce4ec; border-radius: 4px; background: #fff; } |
| | | .user-right { min-height: calc(100vh - 190px); } |
| | | .toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; } |
| | | .selected-count { align-self: center; color: #5c6f82; font-size: 12px; } |
| | | .batch-summary { margin-bottom: 10px; color: #31465d; font-size: 14px; } |
| | | .batch-role-list { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 12px; max-height: 320px; margin-top: 14px; overflow: auto; } |
| | | .pager { margin-top: 12px; text-align: right; } |
| | | .panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 14px; padding-bottom: 10px; border-bottom: 1px solid #edf1f5; } |
| | | .panel-head h3 { margin: 0; color: #31465d; font-size: 15px; } |
| | | .selected-user { color: #5c6f82; font-size: 13px; } |
| | | .empty-tip { padding: 60px 0; color: #93a2b1; text-align: center; } |
| | | .role-list { display: flex; flex-direction: column; gap: 4px; max-height: calc(100vh - 320px); overflow: auto; } |
| | | .role-item { display: flex; align-items: center; height: 34px; margin-right: 0; } |
| | | .role-name { color: #31465d; } |
| | | .role-code { margin-left: 8px; color: #93a2b1; font-size: 12px; } |
| | | .save-button { width: 100%; margin-top: 16px; } |
| | | .role-tag { margin: 2px 4px 2px 0; } |
| | | .muted { color: #a3afbc; } |
| | | @media (max-width: 1100px) { .user-layout { grid-template-columns: 1fr; } } |
| | | </style> |
| New file |
| | |
| | | # 临æ¢ä¿®ç®¡çç³»ç»äº¤æ¥æä»¶ï¼2026.10.3ï¼ |
| | | |
| | | ## åºæ¬ä¿¡æ¯ |
| | | |
| | | - ä»åºç®å½ï¼`D:\projectJava\lqx` |
| | | - Git 忝ï¼`main` |
| | | - åºçº¿æäº¤ï¼`f17c76c fix: load only active report data` |
| | | - åå§éæ±æä»¶ï¼`requirement/æ¹è¿20261003.txt` |
| | | - äº¤æ¥æ´çæ¶é´ï¼2026-10-04 |
| | | - æ¬è½®å·¥ä½æ¥æï¼2026-10-03 |
| | | - æäº¤ç¶æï¼éæ± 11ã12ã17-19 已宿并宿æµè§å¨ãæ¥å£å端å°ç«¯èè°ï¼æ¬è½®ä»£ç 䏿¬ææ¡£å°ç»ä¸æäº¤å¹¶æ¨é `main` 忝ã |
| | | - 注æäºé¡¹ï¼`requirement/æ¹è¿20261003.txt` æ¯ç¨æ·åå§éæ±è®°å½ï¼æ¬æ¬¡æªä¿®æ¹ã |
| | | |
| | | > 2026-10-04 æ´æ°ï¼éæ± 11ã12ã17-19 已宿并宿æµè§å¨ãæ¥å£å端å°ç«¯èè°ï¼æ¬è½®ä»£ç ä¸äº¤æ¥ææ¡£å°ç»ä¸æäº¤å¹¶æ¨é `main` 忝ã`DEPT_NAME` å¹é
çå©ä½é£é©è§ç¬¬åä¸èã |
| | | |
| | | ## ä¸ã鿱宿æ
åµ |
| | | |
| | | | åºå· | éæ± | ç¶æ | 主è¦å®ç° | |
| | | | --- | --- | --- | --- | |
| | | | 1 | logo ä½¿ç¨ `public/logo.png` | 已宿 | `RepairLogo.vue` 读å `${BASE_URL}logo.png`ï¼æä»¶ä½äº `emergency-repair-web/public/logo.png` | |
| | | | 2 | ç¨æ·ç®¡çï¼å·¦å³ç»æï¼å·¦ä¾§ç¨æ·è¡¨æ ¼ãå³ä¾§è§è²å¤éååæ¾ | 已宿 | æ°å¢ `UserManage.vue`ï¼æ¯æç¨æ·åãè´¦æ·åãé¨é¨ãè§è²è¿æ»¤ï¼æ¯æä¸äººå¤è§è² | |
| | | | 3 | ç»å½æ¥å¿ãæä½æ¥å¿ | 已宿 | æ°å¢ç»å½æ¥å¿åæä½æ¥å¿é¡µé¢ãå®ä½ãMapperï¼æä½æ¥å¿è®°å½æ°å¢ãä¿®æ¹ãå é¤è¯·æ± | |
| | | | 4 | èå管çå¢å æé®ç±»åï¼æé®åªè½æå¨èåä¸çº§ | 已宿 | `MenuManage.vue`ã`ButtonPermissionCatalog.java`ï¼é¡µé¢æé®å¯èªå¨è·åå¹¶åæ¥ä¸º `MENU_TYPE=2` | |
| | | | 5 | 代ç ä¸çâè£
å¤âæ¹æâ设å¤â | 已宿 | å端ãå端æºç æ£æ¥æ âè£
å¤âå¹é
ï¼åå§åèåä¹ææ¿æ¢é»è¾ | |
| | | | 6 | å¤ææ¡£æ ç¾æ¨ªåæ»å¨æ¶é«åº¦ä¸è¶³ãæåæ¾ç¤ºä¸å
¨ | 已宿 | `AppLayout.vue` è°æ´å¤ææ¡£æ ç¾æ ãæ»å¨æ¡åæ ç¾é«åº¦ | |
| | | | 7 | æ¾å¼æéï¼å
¶ä»ç¨æ·ä¹å¯ä»¥ç»å½ | 已宿 | `AuthController` æ¥è¯¢å¤é¨ç¨æ·è¡¨ï¼ä¸ååªå
许 adminï¼å
¶ä»ææç¨æ·å¯ç»å½ | |
| | | | 8 | èåæé管çç¶åèç¹å¤éèå¨ | 已宿 | `MenuPermission.vue` ä½¿ç¨ Element UI é»è®¤çº§èå¤éé»è¾ï¼å¹¶å¨ä¿åæ¶å
å«åéç¶èç¹ | |
| | | | 9 | ç¨æ·ç®¡çæ¹é设置è§è²ï¼è§è²ç®¡çå¢å è§è²ç¨æ·è®¾ç½® | 已宿 | ç¨æ·ç®¡çæ¯æâ追å è§è²âåâè¦çè§è²âï¼è§è²ç®¡çæ¯æâç¨æ·è®¾ç½®âåè§è²ç¨æ·åæ¾ | |
| | | | 10 | 产åç»ææ æ¹ä¸ºåä¹è¯è¡¨ `ZT_BOAT_TREE` | 已宿ï¼ä»æå¾
确认äºé¡¹ | å·²æ å° `LABEL`ã`INFO_ID`ã`CONNECT_ID`ã`CONNECT_GROUP`ã`LAYER`ï¼å·²æ¹ä¸ºæå è½½ | |
| | | |
| | | ## äºãç¨æ·ä¸è§è²ææ |
| | | |
| | | ### 2.1 ç¨æ·ç®¡ç |
| | | |
| | | é¡µé¢æä»¶ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/system/UserManage.vue` |
| | | |
| | | åè½ï¼ |
| | | |
| | | - å·¦ä¾§ç¨æ·è¡¨æ ¼æ¾ç¤ºï¼ |
| | | - ç¨æ·å |
| | | - è´¦æ·å |
| | | - æå±é¨é¨ |
| | | - è§è²æé |
| | | - ææºå· |
| | | - ç¶æ |
| | | - æ¯ææç¨æ·åãè´¦æ·åãé¨é¨ãè§è²è¿æ»¤ã |
| | | - éæ©å·¦ä¾§ç¨æ·åï¼å³ä¾§å¤éæ¾ç¤ºè¯¥ç¨æ·æ¥æçè§è²ã |
| | | - ä¸ä¸ªç¨æ·å¯ä»¥åæ¶æ¥æå¤ä¸ªè§è²ã |
| | | - æ¯æå¤éç¨æ·åâæ¹é设置è§è²âï¼ |
| | | - `append`ï¼ä¿çåè§è²ï¼è¿½å å¾éè§è²ã |
| | | - `replace`ï¼ä½¿ç¨å¾éè§è²è¦çåè§è²ã |
| | | - `replace` 䏿ªéæ©è§è²æ¶ï¼ä¼æ¸
空æéç¨æ·çå
¨é¨è§è²ã |
| | | |
| | | ç¸å
³æ¥å£ï¼ |
| | | |
| | | - `GET /api/system/users` |
| | | - `GET /api/system/users/{userId}/roles` |
| | | - `PUT /api/system/users/{userId}/roles` |
| | | - `PUT /api/system/users/roles/batch` |
| | | |
| | | ### 2.2 è§è²ç®¡ç |
| | | |
| | | é¡µé¢æä»¶ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/system/RoleManage.vue` |
| | | |
| | | åè½ï¼ |
| | | |
| | | - æ¯è¡è§è²å¢å âç¨æ·è®¾ç½®âã |
| | | - å¼¹çªä¸å¯è¿æ»¤ç¨æ·ï¼å¤éå¹¶åæ¾å½åè§è²æå±ç¨æ·ã |
| | | - ä¿åè§è²ç¨æ·å
³ç³»æ¶åªæ¿æ¢è¯¥è§è²çç¨æ·å
³ç³»ï¼ä¸å é¤è¿äºç¨æ·æ¥æçå
¶ä»è§è²ã |
| | | |
| | | ç¸å
³æ¥å£ï¼ |
| | | |
| | | - `GET /api/system/roles/{roleId}/users` |
| | | - `PUT /api/system/roles/{roleId}/users` |
| | | |
| | | æå¡å®ç°ï¼ |
| | | |
| | | - `SystemService.roleUsers(...)` |
| | | - `SystemService.saveRoleUsers(...)` |
| | | |
| | | æé®æéï¼ |
| | | |
| | | - `system:roles:assign` |
| | | |
| | | ## ä¸ãæ¥å¿åè½ |
| | | |
| | | ### 3.1 ç»å½æ¥å¿ |
| | | |
| | | å®ç°ä½ç½®ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/auth/AuthController.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/entity/SysLoginLog.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/mapper/SysLoginLogMapper.java` |
| | | - `emergency-repair-web/src/views/system/LoginLogManage.vue` |
| | | |
| | | è®°å½å
å®¹ï¼ |
| | | |
| | | - ç¨æ· ID |
| | | - è´¦æ·å |
| | | - ç¨æ·å |
| | | - é¨é¨ |
| | | - ç»å½ IP |
| | | - ç»å½ç¶æ |
| | | - ç»å½ç»æè¯´æ |
| | | - ç»å½æ¶é´ |
| | | |
| | | æ¥å£ï¼ |
| | | |
| | | - `GET /api/system/login-logs` |
| | | |
| | | ### 3.2 æä½æ¥å¿ |
| | | |
| | | å®ç°ä½ç½®ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/log/OperationLogInterceptor.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/entity/SysOperationLog.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/mapper/SysOperationLogMapper.java` |
| | | - `emergency-repair-web/src/views/system/OperationLogManage.vue` |
| | | |
| | | è®°å½è§åï¼ |
| | | |
| | | - æ¦æª `/api/**` ä¸ç `POST`ã`PUT`ã`DELETE` 请æ±ã |
| | | - ç»å½æ¥å£æé¤ï¼ç±ç»å½æ¥å¿è´è´£ã |
| | | - è®°å½æ¨¡åãæä½ç±»åãç®æ ç±»åãç®æ IDãè¯·æ±æ¹å¼ãè¯·æ± URIãæå/失败ãæä½äººåæ¶é´ã |
| | | - ä¸å¡è¯·æ±å¤±è´¥æ¶ï¼æ¥å¿åå
¥å¼å¸¸ä¸ä¼å½±ååä¸å¡è¯·æ±ã |
| | | - å½åæä½æ¥å¿ç¨æ·å请æ±å¤´ `X-Username`ï¼å¦æè°ç¨æ¹æ²¡æä¼ 该请æ±å¤´ï¼ä¼åéè®°å½ä¸º `admin`ãææºç«¯æç¬¬ä¸æ¹è°ç¨æ¶éè¦æ³¨æè¡¥é½è¯¥è¯·æ±å¤´ã |
| | | |
| | | æ¥å£ï¼ |
| | | |
| | | - `GET /api/system/operation-logs` |
| | | |
| | | ### 3.3 æ¥å¿è¡¨ |
| | | |
| | | ç± `DatabaseBootstrap` åå»ºï¼ |
| | | |
| | | - `LX.T_SYS_LOGIN_LOG` |
| | | - `LX.T_SYS_OPERATION_LOG` |
| | | |
| | | ç´¢å¼ï¼ |
| | | |
| | | - `IX_T_SYS_LOGIN_LOG_TIME` |
| | | - `IX_T_SYS_OPERATION_LOG_TIME` |
| | | - `IX_T_SYS_ROLE_USER_USER` |
| | | |
| | | ## åãèå䏿鮿é |
| | | |
| | | é¡µé¢æä»¶ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/system/MenuManage.vue` |
| | | - `emergency-repair-web/src/views/system/MenuPermission.vue` |
| | | |
| | | æé®ç®å½ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java` |
| | | |
| | | è§åï¼ |
| | | |
| | | - èåç±»åï¼ |
| | | - `0`ï¼ç®å½ |
| | | - `1`ï¼èå |
| | | - `2`ï¼æé® |
| | | - æé®åªè½æå¨èåç±»å `1` çä¸çº§ã |
| | | - èå页é¢ç¹å»â页颿é®âåï¼åç«¯ä»æé®ç®å½èªå¨ååºè¯¥é¡µé¢å¯é
ç½®æé®ã |
| | | - å¾éå¹¶ä¿ååï¼èªå¨çææé®ç±»åèåã |
| | | - æé®ç±»åèåå¯ç»§ç»å¨èåæé管ç䏿æç»è§è²ã |
| | | - èåæéæ 使ç¨ç¶åèç¹èå¨ï¼ä¿åæ¶åæ¶æ¶éå·²éä¸èç¹ååéç¶èç¹ã |
| | | |
| | | ç¸å
³æ¥å£ï¼ |
| | | |
| | | - `GET /api/system/menu/buttons` |
| | | - `POST /api/system/menu/buttons` |
| | | - `GET /api/system/menu-permissions` |
| | | - `POST /api/system/menu-permissions` |
| | | |
| | | ## äºã产åç»ææ |
| | | |
| | | ### 5.1 æ°æ®æº |
| | | |
| | | å®ä½æä»¶ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/entity/ProductNode.java` |
| | | |
| | | æ å°è¡¨ï¼ |
| | | |
| | | ```text |
| | | LX.ZT_BOAT_TREE |
| | | ``` |
| | | |
| | | ç©çè¡¨ï¼ |
| | | |
| | | ```text |
| | | CSICZB_ZB.ZT_BOAT_TREE |
| | | ``` |
| | | |
| | | 已确认 `CSICZB` å `LX` ä¸åæåä¹è¯ã |
| | | |
| | | åæ®µæ å°ï¼ |
| | | |
| | | | å¤é¨è¡¨å段 | Java åæ®µ | 说æ | |
| | | | --- | --- | --- | |
| | | | `INFO_ID` | `id` | ä¸å¡èç¹ä¸»é® | |
| | | | `CONNECT_ID` | `parentId` | ç¶èç¹ ID | |
| | | | `CONNECT_GROUP` | `connectGroup` | ç¥è¾è·¯å¾ | |
| | | | `LABEL` | `name` | 设å¤åç§° | |
| | | | `LAYER` | `layer` | èç¹ç±»å | |
| | | | `SORT` | `sortNo` | æåº | |
| | | | `ISDELETE` | `isDelete` | å 餿 è®° | |
| | | | `ID` | `recordId` | ç©çå
³ç³»è¡ ID | |
| | | |
| | | å·²ç¥å±çº§å
¼å®¹æ å°ï¼ |
| | | |
| | | | `LAYER` | å端å±çº§ | å端类å | |
| | | | --- | --- | --- | |
| | | | `model` | 1 | `MODEL` | |
| | | | `side` | 2 | `X_NO` | |
| | | | `system1` | 3 | `SYSTEM1` | |
| | | | `system2` | 4 | `SYSTEM2` | |
| | | | `equipment` | 5 | `DEVICE` | |
| | | | å
¶ä»å¼ | 6 | åå§å¼å¤§å | |
| | | |
| | | ### 5.2 å®é
æ°æ®æ
åµ |
| | | |
| | | 2026-10-03 DM8 宿µï¼ |
| | | |
| | | - ç©çè¡æ°ï¼12974 |
| | | - å»éå `INFO_ID`ï¼12972 |
| | | - éå¤ `INFO_ID`ï¼2 个 |
| | | - æ ¹èç¹ï¼2 个 |
| | | - `equipment`ï¼12798 |
| | | - `system1`ï¼161 |
| | | - `side`ï¼9 |
| | | - `model`ï¼2 |
| | | - æªç¥å±çº§ï¼`ttt`ã`sss`ã`rrrr`ï¼å 1 个 |
| | | |
| | | ç¤ºä¾æ ¹èç¹ï¼ |
| | | |
| | | - `INFO_ID=1280000000000006486` |
| | | - åç§°ï¼`åå·s44444` |
| | | - `LAYER=model` |
| | | |
| | | ### 5.3 æå è½½æ¹é |
| | | |
| | | ç±äº 12972 个èç¹ä¸æ¬¡æ§å è½½åæ¸²æä¼å¯¼è´æµè§å¨å¡æ»ï¼äº§åæ å·²ç»æ¹ä¸ºæå±å¼å±çº§æå è½½ã |
| | | |
| | | æ°å¢æ¥å£ï¼ |
| | | |
| | | - `GET /api/system/tree/product/roots` |
| | | - `GET /api/system/tree/product/children?parentId={id}` |
| | | - `GET /api/system/tree/product/side-roots?xNo={xNo}` |
| | | - `GET /api/system/tree/product/x-numbers` |
| | | - `GET /api/system/tree/product/selected?ids={id1,id2}` |
| | | |
| | | å端使ç¨ä½ç½®ï¼ |
| | | |
| | | - `TreeManage.vue`ï¼äº§åæ 管ç |
| | | - `ProductTreePicker.vue`ï¼æ
é䏿¥åæ
éç¼è¾ä¸ç设å¤éæ© |
| | | - `FaultReport.vue` |
| | | - `FaultEditDialog.vue` |
| | | - `XcbReview.vue` |
| | | |
| | | å¤çç»æï¼ |
| | | |
| | | - äº§åæ ç®¡çé¦å±åªè¿å 2 ä¸ªæ ¹èç¹ã |
| | | - å±å¼æ ¹èç¹æ¶åªè¯·æ±ç´æ¥åèç¹ã |
| | | - æ
éé¡µé¢ X å·ä¸åéå宿´æ ï¼ç´æ¥è¯·æ± X å·å表ã |
| | | - ç¼è¾æ
éæ¶éè¿âå·²é设å¤âæ¥å£åæ¾è®¾å¤åç§°åè·¯å¾ã |
| | | - äº§åæ ç®¡çéèäºå¤é¨è¡¨ä¸åå¨çâç¼ç âåæ®µã |
| | | - 产åèç¹å é¤éç¨è½¯å é¤ï¼è®¾ç½® `ISDELETE=1`ã |
| | | |
| | | ### 5.4 äº§åæ å¾
确认äºé¡¹ |
| | | |
| | | ç¨æ·å·²è¯´æï¼å®é
ç»æå¹¶éä¸¥æ ¼æ `model -> side -> system1 -> system2 -> equipment` æåï¼å¯è½ç¼ºå° `system2`ï¼ä¸ `system2` ä¸ä¹å¯è½æé `equipment` ç±»åèç¹ã |
| | | |
| | | ç®åå®ç°ä»æä»¥ä¸çº¦æï¼ |
| | | |
| | | - æªç¥ `LAYER` ç®åç»ä¸æ å°ä¸ºç¬¬ 6 级ã |
| | | - å端å½åæå¤§ç¼è¾å±çº§åºå®ä¸º 5ï¼ç¬¬ 6 级èç¹é常åªè½æ¥çï¼ç¹å»âæ°å¢ä¸çº§âçæ¡ä»¶ä¸æ»¡è¶³ã |
| | | - éè¦ç¨æ·åç»ç¡®è®¤æªç¥å±çº§æ¯åªè¯»å±ç¤ºãå½å
¥âå
¶ä»âï¼è¿æ¯å
许继ç»é
ç½®ä¸çº§ã |
| | | - äº§åæ åå
¥çæ¯å¤é¨è¡¨åä¹è¯ï¼æ£å¼ç¯å¢éç¡®è®¤æ°æ®åºè´¦å·å¯¹ `CSICZB_ZB.ZT_BOAT_TREE` çå¢å æ¹æéã |
| | | - `DatabaseBootstrap` ä¸ä»ä¿çæ§ `LX.T_PRODUCT_TREE` 建表å®ä¹ï¼ä½ `ProductNode` å·²ä¸åä½¿ç¨æ§è¡¨ï¼`DataInitializer` ä¹å·²åæ¢åå§åäº§åæ æ¼ç¤ºæ°æ®ã |
| | | |
| | | ## å
ãæå»ºä¸è¿è¡ |
| | | |
| | | ### 6.1 ç¯å¢ |
| | | |
| | | - Javaï¼`C:\Program Files\Java\jdk1.8.0_172` |
| | | - Mavenï¼`D:\solidDir\apache-maven-3.6.1\bin\mvn.cmd` |
| | | - Node/npmï¼`C:\Program Files\nodejs\npm.cmd` |
| | | - å端端å£ï¼`8091` |
| | | - å端端å£ï¼`8082` |
| | | - DM8ï¼`127.0.0.1:5236` |
| | | - DM8 æ°æ®åºè´¦å·ï¼`SYSDBA / SYSDBA` |
| | | - ç»å½ç¤ºä¾ï¼`admin / admin` æ `admin / admin123` |
| | | |
| | | ### 6.2 å端æå»º |
| | | |
| | | ```powershell |
| | | $env:JAVA_HOME='C:\Program Files\Java\jdk1.8.0_172' |
| | | Set-Location 'D:\projectJava\lqx\emergency-repair-server' |
| | | & 'D:\solidDir\apache-maven-3.6.1\bin\mvn.cmd' -B -DskipTests clean package |
| | | ``` |
| | | |
| | | æå»ºç»æï¼æåã |
| | | |
| | | JAR è·¯å¾ï¼ |
| | | |
| | | ```text |
| | | D:\projectJava\lqx\emergency-repair-server\target\emergency-repair-server-1.0.0-SNAPSHOT.jar |
| | | ``` |
| | | |
| | | å¯å¨ç¤ºä¾ï¼ |
| | | |
| | | ```powershell |
| | | Start-Process ` |
| | | -FilePath 'C:\Program Files\Java\jdk1.8.0_172\bin\java.exe' ` |
| | | -ArgumentList '-jar','D:\projectJava\lqx\emergency-repair-server\target\emergency-repair-server-1.0.0-SNAPSHOT.jar' ` |
| | | -WorkingDirectory 'D:\projectJava\lqx\emergency-repair-server' ` |
| | | -WindowStyle Hidden ` |
| | | -PassThru |
| | | ``` |
| | | |
| | | ### 6.3 å端æå»º |
| | | |
| | | ```powershell |
| | | Set-Location 'D:\projectJava\lqx\emergency-repair-web' |
| | | & 'C:\Program Files\nodejs\npm.cmd' run build |
| | | ``` |
| | | |
| | | æå»ºç»æï¼æåã |
| | | |
| | | æå»ºåè¦ï¼ |
| | | |
| | | - `chunk-5be386f4` 约 1.01 MiBã |
| | | - `chunk-vendors` 约 946 KiBã |
| | | - å±äºå
ä½ç§¯åè¦ï¼ä¸é»å¡è¿è¡ã |
| | | |
| | | å¯å¨å¼åæå¡ï¼ |
| | | |
| | | ```powershell |
| | | Set-Location 'D:\projectJava\lqx\emergency-repair-web' |
| | | & 'C:\Program Files\nodejs\npm.cmd' run serve |
| | | ``` |
| | | |
| | | å端代çé
ç½®ï¼ |
| | | |
| | | - `/api` 代çå° `http://127.0.0.1:8091` |
| | | - å端å°åï¼`http://localhost:8082` |
| | | |
| | | ## ä¸ã已宿éªè¯ |
| | | |
| | | ### 7.1 ç¼è¯éªè¯ |
| | | |
| | | - å端ï¼`mvn -B -DskipTests clean package` éè¿ã |
| | | - å端ï¼`npm run build` éè¿ã |
| | | |
| | | ### 7.2 æµè§å¨éªè¯ |
| | | |
| | | è§è²ç®¡çï¼ |
| | | |
| | | - âç¨æ·è®¾ç½®âæé®åå¨ã |
| | | - å¼¹çªåæ¾ 44 ä¸ªç¨æ·ã |
| | | - é¦ä¸ªè§è²éä¸ 6 人ã |
| | | |
| | | ç¨æ·ç®¡çï¼ |
| | | |
| | | - âæ¹é设置è§è²âæé®åå¨ã |
| | | - è§è²ææé¢æ¿åå¨ã |
| | | - è¡¨æ ¼è¿å 20 è¡ã |
| | | |
| | | äº§åæ ï¼ |
| | | |
| | | - é¦å± 2 ä¸ªæ ¹èç¹ã |
| | | - å±å¼ç¬¬ä¸ä¸ªæ ¹èç¹åæ¾ç¤º 5 ä¸ªç´æ¥åèç¹ã |
| | | - X å·æ°é 9 个ã |
| | | - å·²éè®¾å¤æ¥å£è½è¿ååç§°å宿´è·¯å¾ã |
| | | - äº§åæ ç®¡çåæ
ééæ©è®¾å¤å¼¹çªå坿£å¸¸å è½½ï¼æªåºç°æ´æ 渲æå¡æ»ã |
| | | |
| | | éªè¯æªå¾åèæ¬ï¼ |
| | | |
| | | - `.codex-run/role-users-verified.png` |
| | | - `.codex-run/user-manage-verified.png` |
| | | - `.codex-run/product-tree-verified.png` |
| | | - `.codex-run/product-tree-lazy-verified.png` |
| | | - `.codex-run/verify-ui.js` |
| | | - `.codex-run/verify-lazy-tree.js` |
| | | - `.codex-run/verify-picker.js` |
| | | |
| | | ### 7.3 äº¤æ¥æ¶è¿ç¨ç¶æ |
| | | |
| | | 2026-10-04 12:28 éæ°æå»ºå¹¶å¯å¨åï¼å½åè¿è¡ç¶æï¼`8082` å端å¼åæå¡çå¬ä¸ï¼`8091` å端çå¬ä¸ãå¯å¨æ¥å¿ä½äº `.codex-run/backend.out.log` å `.codex-run/backend.err.log`ã |
| | | |
| | | 2026-10-03 æåéªè¯æ¶æ¾è¿è¡ï¼ |
| | | |
| | | - å端端å£ï¼`8091` |
| | | - å端端å£ï¼`8082` |
| | | - Edge è°è¯ç«¯å£ï¼`9222` |
| | | |
| | | éæ°èè°æ¶æç¬¬å
èå½ä»¤å¯å¨å端åå端ã |
| | | |
| | | ## å
«ã主è¦åæ´æä»¶ |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/auth/AuthController.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DatabaseBootstrap.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/Requests.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/entity/ProductNode.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/controller/SystemController.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/log/OperationLogInterceptor.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/entity/SysLoginLog.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/entity/SysOperationLog.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/mapper/SysLoginLogMapper.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/mapper/SysOperationLogMapper.java` |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-web/public/logo.png` |
| | | - `emergency-repair-web/src/components/RepairLogo.vue` |
| | | - `emergency-repair-web/src/components/ProductTreePicker.vue` |
| | | - `emergency-repair-web/src/layout/AppLayout.vue` |
| | | - `emergency-repair-web/src/router/index.js` |
| | | - `emergency-repair-web/src/views/system/UserManage.vue` |
| | | - `emergency-repair-web/src/views/system/RoleManage.vue` |
| | | - `emergency-repair-web/src/views/system/MenuManage.vue` |
| | | - `emergency-repair-web/src/views/system/MenuPermission.vue` |
| | | - `emergency-repair-web/src/views/system/TreeManage.vue` |
| | | - `emergency-repair-web/src/views/system/LoginLogManage.vue` |
| | | - `emergency-repair-web/src/views/system/OperationLogManage.vue` |
| | | - `emergency-repair-web/src/views/fault/FaultReport.vue` |
| | | - `emergency-repair-web/src/views/fault/FaultEditDialog.vue` |
| | | - `emergency-repair-web/src/views/fault/XcbReview.vue` |
| | | |
| | | ## ä¹ãæ¥æå»ºè®® |
| | | |
| | | 1. å
确认 `git status`ï¼ä¸è¦åéå½åæªæäº¤ä¿®æ¹ã |
| | | 2. å
å¯å¨å端ï¼åå¯å¨å端ï¼ç¡®è®¤ `8091` å `8082` å¯è®¿é®ã |
| | | 3. äº§åæ åç»ä¼å
确认æªç¥ `LAYER` çå±ç¤ºåç¼è¾è§åã |
| | | 4. æ£å¼èè°æ¶æ£æ¥æä½æ¥å¿æ¯å¦è½æ¿å°çå® `X-Username`ï¼é¿å
å
¨é¨åé为 `admin`ã |
| | | 5. å¦æè¦æäº§åæ åå
¥å¤é¨åä¹è¯è¡¨ï¼å
ç¡®è®¤æ°æ®åºè´¦å·æéåå¤é¨ç³»ç»å¯¹ `ISDELETE` 软å é¤ç约å®ã |
| | | |
| | | ## åã2026-10-04 è¡¥å
éæ±ï¼13-16ï¼ |
| | | |
| | | | åºå· | éæ± | ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | --- | |
| | | | 13 | ææè¯ codename å¢å `rk_` åç¼ | 已宿 | `public/sensitive-words.json`ã`requirement/ææè¯åå
¸.json` å·²æ´æ°ï¼èåå页颿§ææ¡å·²è¿ç§»ä¸º `rk_`ï¼å端ä¿çæ åç¼å«åå
¼å®¹ | |
| | | | 14 | æ
éç¼å·åç¼åå°å¯é
ç½® | 已宿 | `application.yml` æ°å¢ `fault-code.prefix`ï¼é»è®¤ `åºå®åç¼-`ï¼å®æµç¼å· `åºå®åç¼-001/A-261004-001` | |
| | | | 15 | ææè¡¨èªå¨è®°å½å建人ãä¿®æ¹äºº | 已宿 | æ°å¢ `BaseAuditEntity`ã`CurrentUserInterceptor`ã`AuditMetaObjectHandler`ï¼å®æµå建åå
¥ `codex-create-user`ï¼ä¿®æ¹åå
¥ `codex-update-user` | |
| | | | 16 | ææè¡¨é»è¾å é¤å¹¶èªå¨è¿æ»¤ | 已宿 | èªå»ºè¡¨æ°å¢ `DEL_FLAG`ï¼å®ä½ç»§æ¿ `@TableLogic`ï¼æ¥è¡¨ SQL å·²å¢å è¿æ»¤ï¼å®æµé»è¾å é¤åå表æ¥è¯¢è¿å 0 æ¡ | |
| | | |
| | | è¡¥å
说æï¼ |
| | | |
| | | - 产åç»ææ å¤é¨åä¹è¯è¡¨ `LX.ZT_BOAT_TREE` 沿ç¨èªå·±ç `ISDELETE`ï¼ä¸é¢å¤å¢å `UPDATE_BY`ã`DEL_FLAG`ã |
| | | - æ
éç¼å·åºå·æ¥è¯¢ä¼å
å«é»è¾å é¤è®°å½ï¼é¿å
å¯ä¸ç´¢å¼å²çªã |
| | | - æä½æ¥å¿æ°å¢ãä¿®æ¹ãå é¤å已宿µè½åºã |
| | | |
| | | ## åä¸ã2026-10-04 èè°æ¶å°¾ï¼11ã12ã17-19ï¼ |
| | | |
| | | | åºå· | éæ± | ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | --- | |
| | | | 11 | å®å·¥æ
åµå¤çæ¹ä¸ºâå®å·¥âæ¶ä¸è·³è½¬å®å·¥ä¿¡æ¯å¡«æ¥ | 已宿 | å®å·¥å¤çåªæ´æ°æ´¾å·¥åç¶æï¼å®å·¥å¤çä¸å®å·¥å¡«æ¥ç±ä¸åç¨æ·åå«æä½ | |
| | | | 12 | å®å·¥ä¿¡æ¯å¡«æ¥åªæ¾ç¤ºå·²å®å·¥ãæ¬åä½ãæªå¡«æ¥æ´¾å·¥åï¼å¹¶æ¾ç¤ºå·¥ç¨/项ç®åç§° | 已宿 | éæ©å表æâå®å·¥ç¶æ + ç»å½ç¨æ·æå±åä½ + å°æªå¡«æ¥âéå¶ï¼å¹¶æ¾ç¤ºå·¥ç¨åç§° | |
| | | | 17 | è§è²ç®¡çç¨æ·è®¾ç½®å¢å âå
¨é¨ / 已设置â忢 | 已宿 | `RoleManage.vue` å¢å èå´è¿æ»¤ï¼å·²è®¾ç½®åå
¨é¨å表å¯åæ¢ | |
| | | | 18 | å¢å ç»ç»æºæï¼é¨é¨æ 表ï¼åè½ | 已宿 | èå `/system/organization`ï¼é¡µé¢ `OrganizationManage.vue`ï¼æ°æ®æ¥èª `CSICZB_ZB.SYS_DEPT`ï¼æ¥å£ `/api/system/depts`ï¼å½å 34 个é¨é¨ | |
| | | | 19 | æ¿ä¿®å使¹ä¸ºæ¥è¯¢é¨é¨è¡¨ä¸ `DEPT_TYPE='cj'` çè®°å½ | 已宿 | `SystemService.listContractors()` æ¥è¯¢ cj é¨é¨ï¼é¨é¨ ID/åç§°ç¨äºå®¡æ ¸åæ´¾åï¼æ¿ä¿®åä½é¡µåªè¯»ï¼å½å 7 个 cj é¨é¨ | |
| | | |
| | | ### 11.1 主è¦åæ´æä»¶ |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java` |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/system/OrganizationManage.vue` |
| | | - `emergency-repair-web/src/views/system/ContractorManage.vue` |
| | | - `emergency-repair-web/src/views/completion/CompletionReport.vue` |
| | | - `emergency-repair-web/src/views/system/RoleManage.vue` |
| | | - `emergency-repair-web/src/router/index.js` |
| | | |
| | | ### 11.2 éªè¯ç»æ |
| | | |
| | | - å端 `mvn -B -DskipTests clean package` éè¿ï¼å端 `npm run build` éè¿ã |
| | | - `node .codex-run/verify-items-11-12-17.js`ã`node .codex-run/verify-items-18-19.js` éè¿ã |
| | | - æªå¾ï¼`.codex-run/organization-verified.png`ã`.codex-run/contractor-readonly-verified.png`ã`.codex-run/completion-report-verified.png`ã`.codex-run/role-scope-verified.png`ã |
| | | - 端å°ç«¯ä¸´æ¶æ
é `åºå®åç¼-088/A-261004-001` å·²è·é䏿¥ãXCBå®¡æ ¸åé
âåå®¶1/åå®¶2âãCMB/JCCä¸çº§å®¡æ¹åæ´¾åï¼çæä¸¤å¼ 派工åï¼æ¿ä¿®åä½ä¸º cj é¨é¨ ID/åç§°ã |
| | | - å®å·¥å¡«æ¥å¯è§æ§ï¼`cjyh1-1` ä»
è§åå®¶1派工åï¼`cjyh2-1` ä»
è§åå®¶2派工åï¼`admin` è§ä¸¤å¼ ï¼ä¸´æ¶ `T_FAULT`ã`T_WORK_ORDER` æ°æ®å·²æ¸
çã |
| | | |
| | | ### 11.3 å½åæå¡ |
| | | |
| | | - å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge è°è¯ç«¯å£ `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | |
| | | ### 11.4 æ¥ææ³¨æ |
| | | |
| | | - é管çåå®å·¥å¡«æ¥çåä½å¹é
ç®åæ `DEPT_NAME`ï¼ä¸æ¯ `DEPT_ID`ï¼è¥åºç°ååé¨é¨ï¼åºæ¹ä¸º ID 精确å¹é
ã |
| | | - å岿¼ç¤ºæ´¾å·¥åå¯è½ä»ä¿åæ§ `T_CONTRACTOR` åç§°ï¼é管çå cj ç¨æ·å¯è½çä¸å°ï¼`admin` ä¸ååä½è¿æ»¤å½±åã |
| | | - `.codex-run`ãOffice éæä»¶ä»¥å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` æ¯æ¬å°èè°æä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | |
| | | > |
| | | > **2026-09-28 ç¨æ·ç¡®è®¤ï¼B1-B4ï¼**ï¼æ´¾å·¥åâå®å·¥âä¸é¦é¡µâå·²å®å·¥â为åä¸ç»è®¡ç¶æï¼é¦é¡µä¸æ´¾å·¥åçâå®å·¥å¾
è¯éªâå£å¾ä¸è´ï¼âå¾
å¤ä»¶âå
è®¸ç´æ¥âå®å·¥âï¼ç± AXU15 æäº¤æåååå
¥ï¼âå®å·¥å¾
è¯éªâè¯éªä¸éè¿æ¶åå°âæªå®å·¥âï¼ä¸å设âè¿å·¥âç¶æï¼åå åå
¥ä¿®åè´¨éæè§æå¤æ³¨ã |
| | | > |
| | | > **2026-09-28 ç¨æ·ç¡®è®¤ï¼B5 ä¹åç第 4 æ¡æ°éæ±ï¼**ï¼æ
éç¼å·ä¸å人工填åï¼æ¹ä¸ºç³»ç»èªå¨çæï¼æ ¼å¼ `æ½1åº-Xå·/åç±»-YYMMDD-nnn`ï¼Xå·ä¸º3使°åï¼åç±»åTéä¸ä¸é
ç½®ç A/Bï¼YYMMDD ä¸ºä¸æ¥æ¥æï¼nnn 为åä¸ Xå· + åä¸ Téä¸ä¸ + åä¸ä¸æ¥æ¥æçåºå·ï¼ä» 001 èµ·ï¼ãç³»ç»ç®¡çæ°å¢âå·¥ç¨ä¸ä¸é
ç½®âï¼å段ä»
åç§°ï¼ä¸âTéä¸ä¸é
ç½®âï¼å段为åç§°ãåç±»A/Bï¼ï¼Téä¸ä¸ä¸å·¥ç¨ä¸ä¸çåå¼åæ¹ä¸ºä»é
置读åï¼ä¸åå端硬ç¼ç ãæ´¾åå·ä¿æâæ
éç¼å·/nnâï¼æ¿ä¿®åä½åºå·ä» 01 èµ·ï¼ã |
| | | > **2026-09-28 ç¨æ·ç¡®è®¤ï¼B5 ä¹åç第 4 æ¡æ°éæ±ï¼**ï¼æ
éç¼å·ä¸å人工填åï¼æ¹ä¸ºç³»ç»èªå¨çæï¼æ ¼å¼ `åºå®åç¼-Xå·/åç±»-YYMMDD-nnn`ï¼Xå·ä¸º3使°åï¼åç±»åTéä¸ä¸é
ç½®ç A/Bï¼YYMMDD ä¸ºä¸æ¥æ¥æï¼nnn 为åä¸ Xå· + åä¸ Téä¸ä¸ + åä¸ä¸æ¥æ¥æçåºå·ï¼ä» 001 èµ·ï¼ãç³»ç»ç®¡çæ°å¢âå·¥ç¨ä¸ä¸é
ç½®âï¼å段ä»
åç§°ï¼ä¸âTéä¸ä¸é
ç½®âï¼å段为åç§°ãåç±»A/Bï¼ï¼Téä¸ä¸ä¸å·¥ç¨ä¸ä¸çåå¼åæ¹ä¸ºä»é
置读åï¼ä¸åå端硬ç¼ç ãæ´¾åå·ä¿æâæ
éç¼å·/nnâï¼æ¿ä¿®åä½åºå·ä» 01 èµ·ï¼ã |
| | | > |
| | | > **2026-09-17 ç¨æ·ç¡®è®¤**ï¼AXU5/6/7 ä¸å¼ 审æ¹é¡µå
±éï¼ä¿çå表ãå·æ°ãæ¹é审æ¹ï¼å»æä¿®æ¹ãCMBå·²é
ãéé¡¹ãæ·»å è¿åº¦å¤æ³¨ï¼çé¢ãèååæé®ç»ä¸ä½¿ç¨â审æ¹âï¼JCCå¤é¿é¡µé¢åèåç»ä¸åâJCCå¤é¿å®¡æ¹âï¼æ
éä¿¡æ¯å¢å CMBé¢å¯¼ãJCCå©çãJCCå¤é¿ä¸ç»å®¡æ¹æè§ã审æ¹ç»æï¼éè¿/ä¸éè¿ï¼ãå®¡æ¹æ¶é´å段ï¼å¢å é项çç±ãé项人ãé项æ¶é´åæ®µï¼æ
é䏿¥é¶æ®µçé项ç»ä¸ç±XCBå©çæ§è¡ï¼é项人èªå¨åå½åç»å½XCBå©çï¼é项æ¶é´èªå¨åç³»ç»å½åæ¶é´ã |
| | | > |
| | |
| | | | AXU0 é¦é¡µé¨æ· | å·²æ ¸å¯¹ 2026-09-09ï¼é¡¶é¨æé®åºï¼å³ä¾§ï¼å¨æå®¡æ ¸/JCä¿¡æ¯ï¼+ ä¸é¨æXå·ç»è®¡è¡¨ï¼11åï¼ç¤ºä¾2è¡ï¼é¦è¡å½åç¶æ=å°ä¿®ï¼+ åºé¨ä¸æ ï¼å
¬å/å
±äº«èµæºä¸è½½/常ç¨åè½ï¼ï¼âèªç¹å¨æå®¡æ ¸ââXå·å½åä¿¡æ¯âä¸å±æ¬é¡µå·²å ï¼èåæ å¦è¡åç¬æ ¸å¯¹ï¼TD=èé已确认ï¼å
¬å/å
±äº«èµæºä¸è½½/常ç¨åè½=å 使 ç¾ï¼é¡¶é¨æé®ï¼å¨æå®¡æ ¸/JCä¿¡æ¯ï¼å
容åç»åè®® | å·²æ ¸å¯¹ 2026-09-09 | |
| | | | AXU1 æµç¨æ»è§ | **å·²æ ¸å¯¹ 2026-09-11ï¼2026-09-20 æ´æ°éªæ¶æµç¨**ï¼ç¬¬ä¸ç䏿åå§éæ±ï¼æ¹ä¸ºãç®åæµç¨ + åªåç½ç»çãï¼ç¡®è®¤æµç¨ = Tåæ
é䏿¥ â XCBå©çå®¡æ ¸ï¼åé
å¤ä¸ªæ¿ä¿®åä½ãæåãçæå·¥ç¨åå·ï¼â CMBé¢å¯¼å®¡æ¹ â JCCå©çå®¡æ¹ â JCCå¤é¿å®¡æ¹ â XCBå©çæ´¾åï¼æªæ´¾åæ
éå¾éåä¸é®æ´¾åï¼ææ¿ä¿®åä½çææ´¾å·¥åï¼â å家线ä¸ä¿®ç â å家䏿¥å®å·¥ä¿¡æ¯ â éªæ¶ç³è¯· â è´¨æ£åéªæ¶ â è´¨æ£è´è´£äººéªæ¶ â å®å·¥éªæ¶æå°è¾åº â ä¿®åè´¨éè¯çº§ â éªæ¶å®æ | å·²æ ¸å¯¹ 2026-09-11 / éªæ¶é¾ 2026-09-20 | |
| | | | AXU2 æ°æ®å
导å
¥ | ãå·²è§£å³ 2026-09-14ãåªåç½ç»çãæ å¯¼å
¥åè½ â æ¬é¡µæ´ä½åæ¶ãèåé¡¹âæ°æ®å
导å
¥âå é¤ï¼æ éæ ¸å¯¹ | 已忶 2026-09-14 | |
| | | | AXU3 æ
é䏿¥ | **å·²æ ¸å¯¹ 2026-09-16ï¼2026-09-28 æ
éç¼å·æ¹ä¸ºç³»ç»èªå¨çæ**ï¼æ
éç¼å·ç±ç³»ç»æ `æ½1åº-Xå·/åç±»-YYMMDD-nnn` èªå¨çæï¼ä¸å人工填åï¼å 2026-09-20 çâ人工填åâå£å¾ä½åºï¼ï¼å段/æé®éè¿ï¼å表å¢å â䏿¥æ¥æââ䏿¥åä½âï¼ä»»å¡ç¶æ=å
¨å±ä¸ä¸ªå¼ï¼æªä¸æ¥ / 已䏿¥XCB / ä¸çº§æå / XCBå©çå®¡æ ¸å®æ / CMBé¢å¯¼å®¡æ¹å®æ / JCCå©ç审æ¹å®æ / JCCå¤é¿å·²é
ï¼ï¼æ
éå½±å=å¯é
ç½®åå
¸ï¼ç¤ºä¾â便°âï¼ï¼æ
éæ§è´¨=å¯é
ç½®åå
¸ï¼ç¤ºä¾âé大âï¼ï¼âå¡«åæ
éåææ¥åâ第ä¸çæä¸å®ç°ï¼æ°å¢æ¹è¡¨åå¼ï¼å¸¦é»è®¤å¼ã䏿忮µå产åç»ææ éæ©ï¼åæ³å°åæ®µåæ¶ï¼å é¤éå¶=æªä¸æ¥å¯å é¤ï¼å·²ä¸æ¥ä¸å¯å é¤ï¼ä»»å¡ç¶æä¸ºâä¸çº§æåâæ¶é¤å¤ | å·²æ ¸å¯¹ 2026-09-16 | |
| | | | AXU3 æ
é䏿¥ | **å·²æ ¸å¯¹ 2026-09-16ï¼2026-09-28 æ
éç¼å·æ¹ä¸ºç³»ç»èªå¨çæ**ï¼æ
éç¼å·ç±ç³»ç»æ `åºå®åç¼-Xå·/åç±»-YYMMDD-nnn` èªå¨çæï¼ä¸å人工填åï¼å 2026-09-20 çâ人工填åâå£å¾ä½åºï¼ï¼å段/æé®éè¿ï¼å表å¢å â䏿¥æ¥æââ䏿¥åä½âï¼ä»»å¡ç¶æ=å
¨å±ä¸ä¸ªå¼ï¼æªä¸æ¥ / 已䏿¥XCB / ä¸çº§æå / XCBå©çå®¡æ ¸å®æ / CMBé¢å¯¼å®¡æ¹å®æ / JCCå©ç审æ¹å®æ / JCCå¤é¿å·²é
ï¼ï¼æ
éå½±å=å¯é
ç½®åå
¸ï¼ç¤ºä¾â便°âï¼ï¼æ
éæ§è´¨=å¯é
ç½®åå
¸ï¼ç¤ºä¾âé大âï¼ï¼âå¡«åæ
éåææ¥åâ第ä¸çæä¸å®ç°ï¼æ°å¢æ¹è¡¨åå¼ï¼å¸¦é»è®¤å¼ã䏿忮µå产åç»ææ éæ©ï¼åæ³å°åæ®µåæ¶ï¼å é¤éå¶=æªä¸æ¥å¯å é¤ï¼å·²ä¸æ¥ä¸å¯å é¤ï¼ä»»å¡ç¶æä¸ºâä¸çº§æåâæ¶é¤å¤ | å·²æ ¸å¯¹ 2026-09-16 | |
| | | | AXU4 XCBå©çå®¡æ ¸ | **å·²æ ¸å¯¹ 2026-09-16ï¼2026-09-17 è¡¥å
**ï¼æ¬é¡µå³ç¬¬ 2 æ¥ XCBå©çå®¡æ ¸ï¼é¡µé¢åèååç§°ç»ä¸åâXCBå©çå®¡æ ¸âï¼é¡µé¢å
â审æ¹âææ¡ç»ä¸æ¹ä¸ºâå®¡æ ¸âï¼åé
å¤ä¸ªæ¿ä¿®åä½ãææ¿ä¿®å使åãçæå·¥ç¨åå·å卿¬ç¯èï¼åé
æ¿ä¿®å使¶ï¼åéå表æ¥èªâç³»ç»ç®¡ç > æ¿ä¿®åä½ç®¡çâï¼æâæåºâåå7ä¸ªå¸¸ç¨æ¿ä¿®å使¾ç¤ºï¼éè¿åå³å¯é¢å¨æï¼åç»æ´¾å页æ¾ç¤ºæªæ´¾åæ
éï¼å¾éåä¸é®æ´¾åå¹¶ææ¿ä¿®åä½çææ´¾å·¥åï¼æ¥è¯¢=å·¥ç¨éç¨æ¡ä»¶+æ¿ä¿®åä½/æ
éæ§è´¨/ä»»å¡ç¶æï¼å»âæ·»å è¿åº¦å¤æ³¨ââCMBå·²é
âæé®ï¼è·è¸ªææ¯éå å
¥æ
éä¿¡æ¯ä¸åéï¼âè¦æ±å®å·¥æ¶é´âç»ä¸ä¸ºâè¦æ±å®å·¥æ¥æâï¼ä»»å¡ç¶æ=å
¨å±ä¸ä¸ªå¼ï¼âä¸çº§æåâ为ç¬ç«ç¶æå¼ï¼ä¸â已䏿¥XCBâä¸å¡è¯ä¹ç价使µè½¬è½¨è¿¹åå¼ï¼XCBæ¬ç¯èå®¡æ ¸ä¸éè¿æ¶ä¸è½¬âä¸çº§æåâï¼å
容ä¸åæ ¼èµ°âä¿®æ¹âï¼ä¸ä½ä¸ºæ
éå¤çèµ°âé项âï¼æ
é䏿¥é¶æ®µçé项ç»ä¸ç±XCBå©çæ§è¡ï¼è®°å½é项çç±ãé项人ãé项æ¶é´ï¼é项人èªå¨åå½åç»å½XCBå©çï¼é项æ¶é´èªå¨åç³»ç»å½åæ¶é´ | å·²æ ¸å¯¹ 2026-09-16/17 | |
| | | | AXU5/6/7 ä¸ä¸ªå®¡æ¹é¡µ | **å·²æ ¸å¯¹ 2026-09-17**ï¼ä¸é¡µå
±ç¨åä¸å¥çé¢ååè½ï¼ä¿çå表ãå·æ°ãæ¹é审æ¹ï¼å»æä¿®æ¹ãCMBå·²é
ãéé¡¹ãæ·»å è¿åº¦å¤æ³¨ï¼çé¢ãèååæé®ç»ä¸ä½¿ç¨â审æ¹âï¼AXU7 ç»ä¸åâJCCå¤é¿å®¡æ¹âï¼ä¸é¡µä¸éè¿åç´æ¥éåXCBå©çãä»»å¡ç¶æä¸ºâä¸çº§æåâï¼æ
éä¿¡æ¯å¢å ä¸ç»å®¡æ¹æè§ã审æ¹ç»æãå®¡æ¹æ¶é´å段 | å·²æ ¸å¯¹ 2026-09-17 | |
| | | | AXU8 å·¥ç¨ç®¡çæ»è§ | ç¨æ·è¦æ±æç¼æ ¸å¯¹ï¼2026-09-20ï¼ï¼ç¶æçéå½¢å¼åç»å¤ç | æç¼ 2026-09-20 | |
| | | | AXU9 CMBé¢å¯¼å·²é
| ãæä¸å¤ç 2026-09-20ãä¸çº³å
¥å½åéæ±ï¼ç¶æåå«ä¹æ éç»§ç»ç¡®è®¤ | æä¸å¤ç | |
| | | | AXU10 æ´¾å管ç | **已确认 2026-09-20**ï¼å
¥å£å¨ JCCå¤é¿å®¡æ¹åï¼æ¾ç¤ºæªæ´¾åæ
éï¼å¾éåä¸é®æ´¾åï¼ææ¿ä¿®åä½çææ´¾å·¥åï¼ç¼å·ä¸ºæ
éç¼å· + `/01`ã`/02` çï¼é¤âä¸é®æ´¾åâå¤åæªå¾å
¶ä»ä¸å¡æé®æä¸éè¦ï¼æ
éç¼å·ç±ç³»ç»èªå¨çæï¼`æ½1åº-Xå·/åç±»-YYMMDD-nnn`ï¼ã | 已确认 2026-09-20 | |
| | | | AXU10 æ´¾å管ç | **已确认 2026-09-20**ï¼å
¥å£å¨ JCCå¤é¿å®¡æ¹åï¼æ¾ç¤ºæªæ´¾åæ
éï¼å¾éåä¸é®æ´¾åï¼ææ¿ä¿®åä½çææ´¾å·¥åï¼ç¼å·ä¸ºæ
éç¼å· + `/01`ã`/02` çï¼é¤âä¸é®æ´¾åâå¤åæªå¾å
¶ä»ä¸å¡æé®æä¸éè¦ï¼æ
éç¼å·ç±ç³»ç»èªå¨çæï¼`åºå®åç¼-Xå·/åç±»-YYMMDD-nnn`ï¼ã | 已确认 2026-09-20 | |
| | | | AXU11/12/13 | ãæä¸å¤ç 2026-09-20ãä¸çº³å
¥å½åéæ± | æä¸å¤ç | |
| | | | AXU14 å®å·¥æ
åµå¤ç | **已确认 2026-09-20**ï¼æ´¾ååè¿å
¥æ¬é¡µï¼æ¯ä¸è¡=1个派工åï¼åªå
许å¾é1è¡ï¼å»æâå·¥ç¨ååå·âï¼å¢å âå®å·¥ç¶æâï¼å®å·¥ç¶æåºå®ä¸ºâæªå®å·¥ï¼é»è®¤ï¼/å¾
å¤ä»¶/å®å·¥å¾
è¯éª/å®å·¥âï¼å»æâ导åºââæ·»å è¿åº¦å¤æ³¨ââé项âï¼å¾é1è¡åå¯ç¨âæªå®å·¥/å¾
å¤ä»¶/å®å·¥å¾
è¯éªâæé®è®¾ç½®ç¶æï¼Tåè¯åæä¿®åè´¨éè¯ååä¿®åè´¨éæè§ååæé派工åï¼âå®å·¥âæé®ä»
跳转 AXU15âå®å·¥ä¿¡æ¯ä¸æ¥âï¼AXU15 æåæäº¤åï¼å°å
³è派工åå®å·¥ç¶æå为âå®å·¥âã<br>ãå¾
确认ãæä½è§è²ãåç¶ææé®å¯ç¨æ¡ä»¶ã跳转 AXU15 æ¶æ¯å¦ä¼ å
¥éä¸ç派工å | 已确认 2026-09-20ï¼é¨åç»èå¾
ç¡®è®¤ï¼ | |
| | | | å®å·¥éªæ¶æµç¨ï¼5èåï¼æ ç¬ç«æªå¾ï¼ | **已确认 2026-09-20**ï¼AXU15 æåæäº¤å便¬¡è¿å
¥éªæ¶ç³è¯· â è´¨æ£åéªæ¶ â è´¨æ£è´è´£äººéªæ¶ â å®å·¥éªæ¶æå°è¾åº â ä¿®åè´¨éè¯çº§ï¼ä»
è´¨æ£åéªæ¶ãè´¨æ£è´è´£äººéªæ¶æä¾âéè¿/æåâï¼è´¨æ£åéªæ¶æåéªæ¶ç³è¯·ï¼è´¨æ£è´è´£äººéªæ¶æåè´¨æ£åéªæ¶ï¼éªæ¶ç³è¯·ãå®å·¥éªæ¶æå°è¾åºãä¿®åè´¨éè¯çº§ä¸ºç®åå¨ä½ã<br>ãå¾
确认ãåç¯èæä½è§è²ãæå°è¾åºè¡ä¸ºãä¿®åè´¨éè¯çº§ä¸ AXU14 è¯ååæ®µçå
³ç³» | 已确认 2026-09-20ï¼ç»èå¾
ç¡®è®¤ï¼ | |
| | |
| | | 16. ãå·²è§£å³ 2026-09-16ãXCBå©çå®¡æ ¸ä¸éè¿åçå¤çï¼ä¸è½¬âä¸çº§æåâï¼å
容ä¸åæ ¼æ¶ç±XCBå©çéè¿âä¿®æ¹âæé®ä¿®æ£å
容ï¼ä¸ä½ä¸ºæ
éå¤çæ¶ç¹âé项âã |
| | | 17. ãå·²è§£å³ 2026-09-17ãAXU5/6/7 ä¸å¼ 审æ¹é¡µå
±éæé®ã审æ¹ç¨è¯ã审æ¹å段ç»ï¼ä»¥åâJCCå¤é¿å®¡æ¹âåç§°ç»ä¸ã |
| | | 18. ãå·²è§£å³ 2026-09-17ãæ
é䏿¥é¶æ®µé项ç»ä¸ç±XCBå©çæ§è¡ï¼æ
éä¿¡æ¯å¢å é项çç±ãé项人ãé项æ¶é´å段ï¼é项人èªå¨åå½åç»å½XCBå©çï¼é项æ¶é´èªå¨åç³»ç»å½åæ¶é´ã |
| | | 19. ãæ´æ° 2026-09-28ãæ
éç¼å·æ¹ä¸ºç³»ç»èªå¨çæï¼è§å `æ½1åº-Xå·/åç±»-YYMMDD-nnn`ï¼Xå·3使°åãåç±»åTéä¸ä¸é
ç½®A/Bãnnn æåä¸ Xå·+åä¸Téä¸ä¸+åä¸ä¸æ¥æ¥æä» 001 éå¢ï¼ï¼2026-09-20 çâ人工填åãç³»ç»ä¸èªå¨çæâå£å¾ä½åºã |
| | | 19. ãæ´æ° 2026-09-28ãæ
éç¼å·æ¹ä¸ºç³»ç»èªå¨çæï¼è§å `åºå®åç¼-Xå·/åç±»-YYMMDD-nnn`ï¼Xå·3使°åãåç±»åTéä¸ä¸é
ç½®A/Bãnnn æåä¸ Xå·+åä¸Téä¸ä¸+åä¸ä¸æ¥æ¥æä» 001 éå¢ï¼ï¼2026-09-20 çâ人工填åãç³»ç»ä¸èªå¨çæâå£å¾ä½åºã |
| | | 20. ãå·²è§£å³ 2026-09-20ãAXU14 å®å·¥æ
åµå¤çï¼ä½äºæ´¾åä¹åï¼æ¯ä¸è¡=1个派工åï¼åªå
许å¾é1è¡ï¼å»æâå·¥ç¨ååå·âï¼å¢å âå®å·¥ç¶æâï¼å®å·¥ç¶æ4ç§å¼=æªå®å·¥ï¼é»è®¤ï¼/å¾
å¤ä»¶/å®å·¥å¾
è¯éª/å®å·¥ï¼å»æå¯¼åºãæ·»å è¿åº¦å¤æ³¨ãé项ï¼å¾é1è¡åå¯ç¨âæªå®å·¥/å¾
å¤ä»¶/å®å·¥å¾
è¯éªâæé®è®¾ç½®ç¶æï¼Tåè¯åååä¿®åè´¨éè¯ååæè§ï¼âå®å·¥âæé®ä»
跳转 AXU15ï¼AXU15 æåæäº¤åï¼å°å
³è派工åå®å·¥ç¶æå为âå®å·¥âã |
| | | 21. ãAXU14 å¾
确认ãæä½è§è²ãåç¶æè®¾ç½®æé®çå¯ç¨æ¡ä»¶ã跳转 AXU15 æ¶æ¯å¦ä¼ å
¥éä¸ç派工åã |
| | | 22. ãå·²è§£å³ 2026-09-20ãå®å·¥éªæ¶æµç¨ä¸º5个èåï¼éªæ¶ç³è¯·ãè´¨æ£åéªæ¶ãè´¨æ£è´è´£äººéªæ¶ãå®å·¥éªæ¶æå°è¾åºãä¿®åè´¨éè¯çº§ï¼åªæè´¨æ£åéªæ¶åè´¨æ£è´è´£äººéªæ¶æä¾éè¿/æåï¼è´¨æ£åæåéªæ¶ç³è¯·ï¼è´¨æ£è´è´£äººæåè´¨æ£åéªæ¶ï¼å
¶ä½3个èå为ç®åå¨ä½ã |
| | |
| | | > ç³»ç»è®¾è®¡ååï¼ç¨æ·ç¡®è®¤ 2026-08-25ï¼ï¼ä¸æä¸é¨çæµç¨å¼æï¼å®¡æ¹é¾ä¸å·¥ç¨ç¶æåç¨ä¸å¡è¡¨ä¸ç**ç¶æåæ®µ**ç®¡çæµè½¬ï¼ç±ä¸å¡æä½è§¦åå¹¶å¨ä»£ç 䏿 ¡éªåæ³æ§ã |
| | | > ææ¯è·¯çº¿ï¼ç¨æ·ç¡®è®¤ 2026-08-28 / 2026-09-20ï¼ï¼å端 **Vue 2 + Element UI**ï¼å端 **Spring Boot + JDK 8 + MyBatis-Plus 3.5.3**ï¼åè `MyProject\trafficAudit`ãæ°æ®åºä»
å¼å**达梦8ï¼DM8ï¼**ï¼ä¸å¼å MySQLï¼å°å `127.0.0.1:5236`ï¼ç¨æ·å `SYSDBA`ï¼å¯ç `SYSDBA`ï¼æ°æ®åºå¤§å°åææï¼è¡¨åååæ®µåç»ä¸ä½¿ç¨å¤§åã临æ¢ä¿®æ¨¡å¼ `LX` éè¦æ°å»ºï¼`SYS_DEPT`ã`SYS_USER` ä½¿ç¨ `CSICZB_ZB` 模å¼ä¸çåå表并éè¿åä¹è¯è®¿é®ã详ç»çº¦æè§ `requirement\ææ¯è·¯çº¿.md`ã |
| | | > æ´æ°è¯´æï¼2026-09-16ï¼ï¼AXU3 æ
é䏿¥ãAXU4 XCBå©çå®¡æ ¸å·²æ ¸å¯¹å¹¶åºåï¼ä»»å¡ç¶æç»ä¸ä¸ºä¸ä¸ªå¼ï¼åå
æ + ä¸çº§æåï¼ï¼H 已纳å
¥ææè¯åå
¸ï¼é坿æç»ä¸åâæ¯å¦æ¶Hâã |
| | | > æ´æ°è¯´æï¼2026-09-20ï¼ï¼JCCå¤é¿å®¡æ¹å®æåç´æ¥è¿å
¥XCBå©çæ´¾å页é¢ï¼é¡µé¢æ¾ç¤ºæªæ´¾åæ
éï¼å¾éåç¹å»âä¸é®æ´¾åâï¼ææ¿ä¿®åä½çææ´¾å·¥åï¼æ´¾å·¥åç¼å·ä¸ºæ
éç¼å· + `/01`ã`/02` çï¼é¤âä¸é®æ´¾åâå¤ï¼å AXU10 å
¶ä»ä¸å¡æé®æä¸éè¦ï¼AXU8 æç¼æ ¸å¯¹ï¼æ
éç¼å·ç±ç³»ç»æ `æ½1åº-Xå·/åç±»-YYMMDD-nnn` èªå¨çæï¼2026-09-28 å£å¾æ´æ°ï¼æ¿ä»£åâ人工填åâï¼ï¼æ
éä¿¡æ¯ä¿çâæ
éç¼å·âåæ®µãAXU14âå®å·¥æ
åµå¤çâä½äºæ´¾åä¹åï¼æ¯ä¸è¡ä¸º1个派工åï¼åªå
许å¾é1è¡ï¼åè¡¨å»æâå·¥ç¨ååå·âãå¢å âå®å·¥ç¶æâï¼å»æâ导åºââæ·»å è¿åº¦å¤æ³¨ââé项âæé®ï¼æ´¾å·¥åå®å·¥ç¶æåºå®ä¸ºâæªå®å·¥ï¼é»è®¤ï¼/å¾
å¤ä»¶/å®å·¥å¾
è¯éª/å®å·¥âåç§ï¼å¾éåå¯ç¨ä¸ä¸ªç¶ææé®è®¾ç½®âæªå®å·¥/å¾
å¤ä»¶/å®å·¥å¾
è¯éªâï¼âå®å·¥âæé®åªè·³è½¬AXU15ï¼ä¸ç«å³æ¹ç¶æï¼AXU15æåæäº¤åå°å
³è派工åå®å·¥ç¶æå为âå®å·¥âï¼éåè¿å
¥å®å·¥éªæ¶æµç¨ï¼éªæ¶ç³è¯· â è´¨æ£åéªæ¶ â è´¨æ£è´è´£äººéªæ¶ â å®å·¥éªæ¶æå°è¾åº â ä¿®åè´¨éè¯çº§ï¼ä»
è´¨æ£åéªæ¶ãè´¨æ£è´è´£äººéªæ¶æä¾éè¿/æåï¼è´¨æ£åæåéªæ¶ç³è¯·ï¼è´¨æ£è´è´£äººæåè´¨æ£åéªæ¶ï¼å
¶ä½3个èå为ç®åå¨ä½ï¼Tåè¯åååä¿®åè´¨éè¯ååæè§ãå¦ç¡®è®¤ï¼ç³»ç»ç®¡çä¸çº§èåå
嫿¿ä¿®åä½ç®¡çã产åç»ææ 管çã设å¤åºç¡åºç®¡çï¼æ¿ä¿®åä½å段为åç§°ãç®ç§°ãå使§è´¨ãæåºãèç³»äººãæ¯å¦å¤±ä¿¡åä½ï¼æåºç¨äºXCBå©çå®¡æ ¸æ¶æ¾ç¤ºå7ä¸ªå¸¸ç¨æ¿ä¿®åä½ï¼äº§åç»ææ 为åå·âXå·âä¸çº§ç³»ç»âäºçº§ç³»ç»â设å¤ç5级æ ï¼è®¾å¤åºç¡åºä¸ºä¸çº§ç³»ç»âäºçº§ç³»ç»â设å¤ç3级æ ï¼äº§åç»ææ 设å¤ä¸ºå®ä¾ï¼è®¾å¤åºç¡åºè®¾å¤ä¸ºç±»ã |
| | | > æ´æ°è¯´æï¼2026-09-20ï¼ï¼JCCå¤é¿å®¡æ¹å®æåç´æ¥è¿å
¥XCBå©çæ´¾å页é¢ï¼é¡µé¢æ¾ç¤ºæªæ´¾åæ
éï¼å¾éåç¹å»âä¸é®æ´¾åâï¼ææ¿ä¿®åä½çææ´¾å·¥åï¼æ´¾å·¥åç¼å·ä¸ºæ
éç¼å· + `/01`ã`/02` çï¼é¤âä¸é®æ´¾åâå¤ï¼å AXU10 å
¶ä»ä¸å¡æé®æä¸éè¦ï¼AXU8 æç¼æ ¸å¯¹ï¼æ
éç¼å·ç±ç³»ç»æ `åºå®åç¼-Xå·/åç±»-YYMMDD-nnn` èªå¨çæï¼2026-09-28 å£å¾æ´æ°ï¼æ¿ä»£åâ人工填åâï¼ï¼æ
éä¿¡æ¯ä¿çâæ
éç¼å·âåæ®µãAXU14âå®å·¥æ
åµå¤çâä½äºæ´¾åä¹åï¼æ¯ä¸è¡ä¸º1个派工åï¼åªå
许å¾é1è¡ï¼åè¡¨å»æâå·¥ç¨ååå·âãå¢å âå®å·¥ç¶æâï¼å»æâ导åºââæ·»å è¿åº¦å¤æ³¨ââé项âæé®ï¼æ´¾å·¥åå®å·¥ç¶æåºå®ä¸ºâæªå®å·¥ï¼é»è®¤ï¼/å¾
å¤ä»¶/å®å·¥å¾
è¯éª/å®å·¥âåç§ï¼å¾éåå¯ç¨ä¸ä¸ªç¶ææé®è®¾ç½®âæªå®å·¥/å¾
å¤ä»¶/å®å·¥å¾
è¯éªâï¼âå®å·¥âæé®åªè·³è½¬AXU15ï¼ä¸ç«å³æ¹ç¶æï¼AXU15æåæäº¤åå°å
³è派工åå®å·¥ç¶æå为âå®å·¥âï¼éåè¿å
¥å®å·¥éªæ¶æµç¨ï¼éªæ¶ç³è¯· â è´¨æ£åéªæ¶ â è´¨æ£è´è´£äººéªæ¶ â å®å·¥éªæ¶æå°è¾åº â ä¿®åè´¨éè¯çº§ï¼ä»
è´¨æ£åéªæ¶ãè´¨æ£è´è´£äººéªæ¶æä¾éè¿/æåï¼è´¨æ£åæåéªæ¶ç³è¯·ï¼è´¨æ£è´è´£äººæåè´¨æ£åéªæ¶ï¼å
¶ä½3个èå为ç®åå¨ä½ï¼Tåè¯åååä¿®åè´¨éè¯ååæè§ãå¦ç¡®è®¤ï¼ç³»ç»ç®¡çä¸çº§èåå
嫿¿ä¿®åä½ç®¡çã产åç»ææ 管çã设å¤åºç¡åºç®¡çï¼æ¿ä¿®åä½å段为åç§°ãç®ç§°ãå使§è´¨ãæåºãèç³»äººãæ¯å¦å¤±ä¿¡åä½ï¼æåºç¨äºXCBå©çå®¡æ ¸æ¶æ¾ç¤ºå7ä¸ªå¸¸ç¨æ¿ä¿®åä½ï¼äº§åç»ææ 为åå·âXå·âä¸çº§ç³»ç»âäºçº§ç³»ç»â设å¤ç5级æ ï¼è®¾å¤åºç¡åºä¸ºä¸çº§ç³»ç»âäºçº§ç³»ç»â设å¤ç3级æ ï¼äº§åç»ææ 设å¤ä¸ºå®ä¾ï¼è®¾å¤åºç¡åºè®¾å¤ä¸ºç±»ã |
| | | > æ´æ°è¯´æï¼2026-09-17ï¼ï¼AXU5/6/7 ä¸å¼ 审æ¹é¡µæå
±éè§åæ ¸å¯¹å®æï¼ä¿çå表ãå·æ°ãæ¹é审æ¹ï¼å»æä¿®æ¹ãCMBå·²é
ãéé¡¹ãæ·»å è¿åº¦å¤æ³¨ï¼ç»ä¸â审æ¹âç¨è¯åâJCCå¤é¿å®¡æ¹âåç§°ï¼æ
éä¿¡æ¯å¢å ä¸ç»å®¡æ¹æè§ã审æ¹ç»æãå®¡æ¹æ¶é´å段ï¼ä»¥åé项çç±ãé项人ãé项æ¶é´å段ãæ
é䏿¥é¶æ®µçé项ç»ä¸ç±XCBå©çæ§è¡ã |
| | | > æ´æ°è¯´æï¼2026-09-20 ç»è®¡æ¥è¡¨ï¼ï¼æ°å¢ä¸çº§èåâç»è®¡æ¥è¡¨âï¼è¯å«è£
夿
éæ»ä½åæãæ
éé«åè®¾å¤ TopãæTç»è®¡ãæ
éç±»ååå¸ãåå®¶ç»´ä¿®æ
åµãæ
éæ°/å¤åå¤ä»¶æ¶è年度è¶å¿6项åè½ï¼å
¶ä¸âæ
éç±»ååå¸âå éæ±æä¸æç¡®èæç¼ï¼è£
夿
éæ»ä½åæä½¿ç¨âçæµé¡¹ç®âï¼å家䏿¿ä¿®åä½åä¹ï¼ç³»ç»ç®¡çä¸çâåºç¡è®¾å¤åºâç»ä¸æ´å为â设å¤åºç¡åºç®¡çâã |
| | | > æ´æ°è¯´æï¼2026-09-28 B1-B4ï¼ï¼æ´¾å·¥åâå®å·¥âä¸é¦é¡µâå·²å®å·¥â为åä¸ç»è®¡ç¶æï¼é¦é¡µä¸æ´¾å·¥åçâå®å·¥å¾
è¯éªâå£å¾ä¸è´ï¼âå¾
å¤ä»¶âå¯ç´æ¥âå®å·¥âï¼âå®å·¥å¾
è¯éªâè¯éªä¸éè¿åâæªå®å·¥âï¼ä¸æ°å¢âè¿å·¥âç¶æã |
| | |
| | | - å é¤éå¶ï¼2026-09-16 确认ï¼ï¼æªä¸æ¥çæ
éå¯ä»¥å é¤ï¼å·²ä¸æ¥çæ
éä¸è½å é¤ï¼ä»»å¡ç¶æä¸ºâä¸çº§æåâæ¶ä»å
许å é¤ã |
| | | - ã已确认 2026-09-16ã第ä¸çæä¸å®ç°âå¡«åæ
éåææ¥åâï¼ä¸å¡é»è¾å¾
åç»æç¡®ã |
| | | - å表ï¼å·¥ç¨éç¨å + 䏿¥æ¥æã䏿¥åä½ + ä»»å¡ç¶æã |
| | | - æ
éç¼å·ï¼2026-09-28 ç¡®è®¤ï¼æ¿ä»£ 2026-09-20 å£å¾ï¼ï¼ç±ç³»ç»èªå¨çæï¼è§å `æ½1åº-Xå·/åç±»-YYMMDD-nnn`ï¼å
¶ä¸ Xå·ä¸º3使°åãåç±»åâTéä¸ä¸é
ç½®âç A/BãYYMMDD ä¸ºä¸æ¥æ¥æãnnn 为åä¸ Xå·+åä¸Téä¸ä¸+åä¸ä¸æ¥æ¥æçåºå·ï¼ä» 001 èµ·ï¼ï¼çæåä¸å¯ä¿®æ¹ï¼ç¨äºçææ´¾å·¥åç¼å·ã |
| | | - æ
éç¼å·ï¼2026-09-28 ç¡®è®¤ï¼æ¿ä»£ 2026-09-20 å£å¾ï¼ï¼ç±ç³»ç»èªå¨çæï¼è§å `åºå®åç¼-Xå·/åç±»-YYMMDD-nnn`ï¼å
¶ä¸ Xå·ä¸º3使°åãåç±»åâTéä¸ä¸é
ç½®âç A/BãYYMMDD ä¸ºä¸æ¥æ¥æãnnn 为åä¸ Xå·+åä¸Téä¸ä¸+åä¸ä¸æ¥æ¥æçåºå·ï¼ä» 001 èµ·ï¼ï¼çæåä¸å¯ä¿®æ¹ï¼ç¨äºçææ´¾å·¥åç¼å·ã |
| | | - ä»»å¡ç¶æç»ä¸åå¼ï¼2026-09-16 确认ï¼ï¼æªä¸æ¥ / 已䏿¥XCB / ä¸çº§æå / XCBå©çå®¡æ ¸å®æ / CMBé¢å¯¼å®¡æ¹å®æ / JCCå©ç审æ¹å®æ / JCCå¤é¿å·²é
ã |
| | | - æ°å¢/ç¼è¾è¡¨ååæ®µï¼æ
éç¼å·ï¼åªè¯»ï¼ä¿ååèªå¨çæï¼ãXå·ã䏿¥åä½ãTéä¸ä¸ãè´è´£äººã䏿¥æ¥æãå·¥ç¨ä¸ä¸ãè£
å¤åç§°ãåæº/é¨ä½ãæå±ä¸çº§ç³»ç»ãå·¥ç¨åç§°ãä¿®çææ¯è¦æ±ãè·è¸ªææ¯éï¼åéï¼ä¸é/äºé/ä¸éï¼ãæ
éå½±åãæ
éæ§è´¨ãé大æ
éæè¿°ãæ¯å¦èªä¿®ãæ¯å¦æ¶Hãçæµé¡¹ç®ãçæµæ°å¼ãè¦æ±å®å·¥æ¥æã夿³¨ã |
| | | - ã已确认 2026-09-16ãæ°å¢æ
éæ¹ä¸ºè¡¨åå¼å½å
¥ï¼ä¸ä½¿ç¨åéæ±çè¡¨æ ¼å¼å½å
¥ã |
| | |
| | | 18. ãå·²è§£å³ 2026-09-16ãXCBå©çå®¡æ ¸ä¸éè¿åçå¤çï¼ä¸è½¬âä¸çº§æåâï¼å
容ä¸åæ ¼æ¶ç±XCBå©çéè¿âä¿®æ¹âæé®ä¿®æ£å
容ï¼ä¸ä½ä¸ºæ
éå¤çæ¶ç¹âé项âã |
| | | 19. ãå·²è§£å³ 2026-09-17ãAXU5/6/7 ä¸å¼ 审æ¹é¡µå
±éæé®ã审æ¹ç¨è¯åä¸ç»å®¡æ¹å段ã |
| | | 20. ãå·²è§£å³ 2026-09-17ãæ
é䏿¥é¶æ®µé项ç»ä¸ç±XCBå©çæ§è¡ï¼æ
éä¿¡æ¯å¢å é项çç±ãé项人ãé项æ¶é´å段ï¼é项人èªå¨åå½åç»å½XCBå©çï¼é项æ¶é´èªå¨åç³»ç»å½åæ¶é´ã |
| | | 21. ãæ´æ° 2026-09-28ãæ
éç¼å·ç±ç³»ç»èªå¨çæï¼`æ½1åº-Xå·/åç±»-YYMMDD-nnn`ï¼ï¼ç³»ç»ç®¡çæ°å¢âå·¥ç¨ä¸ä¸é
ç½®ââTéä¸ä¸é
ç½®âï¼ä¸¤å¤ä¸ææ¹ç±é
置读åã |
| | | 21. ãæ´æ° 2026-09-28ãæ
éç¼å·ç±ç³»ç»èªå¨çæï¼`åºå®åç¼-Xå·/åç±»-YYMMDD-nnn`ï¼ï¼ç³»ç»ç®¡çæ°å¢âå·¥ç¨ä¸ä¸é
ç½®ââTéä¸ä¸é
ç½®âï¼ä¸¤å¤ä¸ææ¹ç±é
置读åã |
| | | 22. ãAXU14 å¾
ç¡®è®¤ãæ¬é¡µæä½è§è²ï¼åç¶æè®¾ç½®æé®çå¯ç¨æ¡ä»¶ï¼è·³è½¬ AXU15 æ¶æ¯å¦ä¼ å
¥éä¸ç派工åã |
| | | 23. ãå·²è§£å³ 2026-09-20ãå®å·¥éªæ¶æµç¨ä¸º5个èåï¼éªæ¶ç³è¯·ãè´¨æ£åéªæ¶ãè´¨æ£è´è´£äººéªæ¶ãå®å·¥éªæ¶æå°è¾åºãä¿®åè´¨éè¯çº§ãåªæè´¨æ£åéªæ¶åè´¨æ£è´è´£äººéªæ¶æä¾éè¿/æåï¼è´¨æ£åæåéªæ¶ç³è¯·ï¼è´¨æ£è´è´£äººæåè´¨æ£åéªæ¶ï¼éªæ¶ç³è¯·ãå®å·¥éªæ¶æå°è¾åºãä¿®åè´¨éè¯çº§ä¸ºç®åå¨ä½ã |
| | | 24. ãå®å·¥éªæ¶å¾
确认ãåç¯èæä½è§è²ï¼å®å·¥éªæ¶æå°è¾åºçå®é
æå°è¡ä¸ºï¼âä¿®åè´¨éè¯çº§âä¸ AXU14âTåè¯åâåæ®µçå
³ç³»ã |
| | |
| | | > 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 ç»§ç»æç¼ã |
| | | > 2026-09-20 æ´æ°ï¼JCCå¤é¿å®¡æ¹å®æåç´æ¥è¿å
¥ XCBå©çæ´¾å页é¢ï¼é¡µé¢æ¾ç¤ºæªæ´¾åæ
éï¼å¾éåä¸é®æ´¾åï¼ææ¿ä¿®åä½çææ´¾å·¥åï¼ç¼å·ä¸ºæ
éç¼å· + `/01`ã`/02` çï¼é¤âä¸é®æ´¾åâå¤åæªå¾å
¶ä»ä¸å¡æé®æä¸éè¦ï¼æ
éç¼å·ç±ç³»ç»æ `æ½1åº-Xå·/åç±»-YYMMDD-nnn` èªå¨çæï¼2026-09-28 æ´æ°ï¼æ¿ä»£åâ人工填åâï¼ãAXU8 æç¼æ ¸å¯¹ãAXU14 ä½äºæ´¾åä¹åï¼æ¯ä¸è¡=1个派工åï¼åªå
许å¾é1è¡ï¼å®å·¥ç¶æåºå®ä¸ºâæªå®å·¥ï¼é»è®¤ï¼/å¾
å¤ä»¶/å®å·¥å¾
è¯éª/å®å·¥âï¼å»æå¯¼åºãæ·»å è¿åº¦å¤æ³¨ãé项æé®ï¼Tåè¯åååä¿®åè´¨éè¯ååæè§ï¼âå®å·¥âæé®ä»
跳转 AXU15ï¼AXU15 æåæäº¤åï¼å°å
³è派工åå®å·¥ç¶æå为âå®å·¥âï¼éåè¿å
¥5èåå®å·¥éªæ¶æµç¨ï¼éªæ¶ç³è¯· â è´¨æ£åéªæ¶ â è´¨æ£è´è´£äººéªæ¶ â å®å·¥éªæ¶æå°è¾åº â ä¿®åè´¨éè¯çº§ï¼ä»
è´¨æ£åéªæ¶ãè´¨æ£è´è´£äººéªæ¶æä¾éè¿/æåï¼è´¨æ£åæåéªæ¶ç³è¯·ï¼è´¨æ£è´è´£äººæåè´¨æ£åéªæ¶ï¼éªæ¶ç³è¯·ãå®å·¥éªæ¶æå°è¾åºãä¿®åè´¨éè¯çº§ä¸ºç®åå¨ä½ã |
| | | > 2026-09-20 æ´æ°ï¼JCCå¤é¿å®¡æ¹å®æåç´æ¥è¿å
¥ XCBå©çæ´¾å页é¢ï¼é¡µé¢æ¾ç¤ºæªæ´¾åæ
éï¼å¾éåä¸é®æ´¾åï¼ææ¿ä¿®åä½çææ´¾å·¥åï¼ç¼å·ä¸ºæ
éç¼å· + `/01`ã`/02` çï¼é¤âä¸é®æ´¾åâå¤åæªå¾å
¶ä»ä¸å¡æé®æä¸éè¦ï¼æ
éç¼å·ç±ç³»ç»æ `åºå®åç¼-Xå·/åç±»-YYMMDD-nnn` èªå¨çæï¼2026-09-28 æ´æ°ï¼æ¿ä»£åâ人工填åâï¼ãAXU8 æç¼æ ¸å¯¹ãAXU14 ä½äºæ´¾åä¹åï¼æ¯ä¸è¡=1个派工åï¼åªå
许å¾é1è¡ï¼å®å·¥ç¶æåºå®ä¸ºâæªå®å·¥ï¼é»è®¤ï¼/å¾
å¤ä»¶/å®å·¥å¾
è¯éª/å®å·¥âï¼å»æå¯¼åºãæ·»å è¿åº¦å¤æ³¨ãé项æé®ï¼Tåè¯åååä¿®åè´¨éè¯ååæè§ï¼âå®å·¥âæé®ä»
跳转 AXU15ï¼AXU15 æåæäº¤åï¼å°å
³è派工åå®å·¥ç¶æå为âå®å·¥âï¼éåè¿å
¥5èåå®å·¥éªæ¶æµç¨ï¼éªæ¶ç³è¯· â è´¨æ£åéªæ¶ â è´¨æ£è´è´£äººéªæ¶ â å®å·¥éªæ¶æå°è¾åº â ä¿®åè´¨éè¯çº§ï¼ä»
è´¨æ£åéªæ¶ãè´¨æ£è´è´£äººéªæ¶æä¾éè¿/æåï¼è´¨æ£åæåéªæ¶ç³è¯·ï¼è´¨æ£è´è´£äººæåè´¨æ£åéªæ¶ï¼éªæ¶ç³è¯·ãå®å·¥éªæ¶æå°è¾åºãä¿®åè´¨éè¯çº§ä¸ºç®åå¨ä½ã |
| | | > 2026-09-28 æ´æ°ï¼B1-B4ï¼ï¼æ´¾å·¥åâå®å·¥âä¸é¦é¡µâå·²å®å·¥â为åä¸ç»è®¡ç¶æï¼ä¸¤å¤âå®å·¥å¾
è¯éªâå£å¾ä¸è´ï¼âå¾
å¤ä»¶âå
è®¸ç´æ¥âå®å·¥âï¼âå®å·¥å¾
è¯éªâè¯éªä¸éè¿åâæªå®å·¥âï¼ä¸æ°å¢âè¿å·¥âç¶æã |
| | | > æ§è´¨ï¼éæ±æ¢³çé¶æ®µç**è®¾è®¡èæ¡**ï¼å¾
ç¨æ·ç¡®è®¤ååºåè¿éæ±æ¸
åã |
| | | |
| | |
| | | - **ä»»å¡ç¶æ**ï¼æ´¾å䏿¹åä»»å¡ç¶æï¼ä»ç¶ä¿æâJCCå¤é¿å·²é
âï¼âæªæ´¾å/已派åâæ¯æ´¾åç¯èçç¬ç«æ¥è¯¢å£å¾ã |
| | | - **å
³èè§å**ï¼XCBå©çå®¡æ ¸ä»è´è´£åé
æ¿ä¿®åä½ãæåå¹¶çæå·¥ç¨åå·ï¼æ´¾åæ¶ä¸åéæ°åé
æä¿®æ¹æ¿ä¿®åä½ã |
| | | - **å¸¸ç¨æ¿ä¿®å使¾ç¤º**ï¼XCBå©çå®¡æ ¸åé
æ¿ä¿®å使¶ï¼åéå表æ¥èªâç³»ç»ç®¡ç > æ¿ä¿®åä½ç®¡çâï¼æâæåºâæåºåæ¾ç¤ºå7ä¸ªå¸¸ç¨æ¿ä¿®åä½ã |
| | | - **æ
éç¼å·æ¥æº**ï¼2026-09-28 æ´æ°ï¼ï¼ç±ç³»ç»èªå¨çæï¼è§å `æ½1åº-Xå·/åç±»-YYMMDD-nnn`ï¼Xå·3使°åãåç±»åâTéä¸ä¸é
ç½®âç A/BãYYMMDD ä¸ºä¸æ¥æ¥æãnnn 为åä¸ Xå·+åä¸Téä¸ä¸+åä¸ä¸æ¥æ¥æçåºå·ï¼ä» 001 èµ·ï¼ï¼æ
éä¿¡æ¯ä¿çâæ
éç¼å·âåæ®µï¼çæåä¸å¯ä¿®æ¹ã |
| | | - **æ
éç¼å·æ¥æº**ï¼2026-09-28 æ´æ°ï¼ï¼ç±ç³»ç»èªå¨çæï¼è§å `åºå®åç¼-Xå·/åç±»-YYMMDD-nnn`ï¼Xå·3使°åãåç±»åâTéä¸ä¸é
ç½®âç A/BãYYMMDD ä¸ºä¸æ¥æ¥æãnnn 为åä¸ Xå·+åä¸Téä¸ä¸+åä¸ä¸æ¥æ¥æçåºå·ï¼ä» 001 èµ·ï¼ï¼æ
éä¿¡æ¯ä¿çâæ
éç¼å·âåæ®µï¼çæåä¸å¯ä¿®æ¹ã |
| | | |
| | | ## 3. å·¥ç¨ç¶ææºï¼æ§è¡é¾è·¯ï¼ |
| | | |
| | |
| | | 13. ãå·²è§£å³ 2026-09-16ãXCBå©çå®¡æ ¸ä¸éè¿åçå¤çï¼ä¸è½¬âä¸çº§æåâï¼å
容ä¸åæ ¼æ¶ç±XCBå©çéè¿âä¿®æ¹âæé®ä¿®æ£å
容ï¼ä¸ä½ä¸ºæ
éå¤çæ¶ç¹âé项âãâä¸çº§æåâä»
éç¨äºCMBé¢å¯¼ãJCCå©çãJCCå¤é¿éåXCBå©çã |
| | | 14. ãå·²è§£å³ 2026-09-17ãAXU5/6/7 å
±éæä½ã审æ¹ç¨è¯å审æ¹å段ç»ï¼ä¿çå表ãå·æ°ãæ¹é审æ¹ï¼å»æä¿®æ¹ãCMBå·²é
ãéé¡¹ãæ·»å è¿åº¦å¤æ³¨ï¼ç»ä¸â审æ¹âç¨è¯åâJCCå¤é¿å®¡æ¹âåç§°ï¼å¢å CMBé¢å¯¼/JCCå©ç/JCCå¤é¿ä¸ç»å®¡æ¹æè§ã审æ¹ç»æãå®¡æ¹æ¶é´å段ã |
| | | 15. ãå·²è§£å³ 2026-09-17ãæ
é䏿¥é¶æ®µé项ï¼ç»ä¸ç±XCBå©çæ§è¡ï¼æ
éä¿¡æ¯å¢å é项çç±ãé项人ãé项æ¶é´å段ï¼é项人èªå¨åå½åç»å½XCBå©çï¼é项æ¶é´èªå¨åç³»ç»å½åæ¶é´ã |
| | | 16. ãæ´æ° 2026-09-28ãæ
éç¼å·ç±ç³»ç»èªå¨çæï¼`æ½1åº-Xå·/åç±»-YYMMDD-nnn`ï¼ï¼ç³»ç»ç®¡çæ°å¢âå·¥ç¨ä¸ä¸é
ç½®ââTéä¸ä¸é
ç½®âï¼Téä¸ä¸/å·¥ç¨ä¸ä¸ä¸ææ¹ç±é
置读åã |
| | | 16. ãæ´æ° 2026-09-28ãæ
éç¼å·ç±ç³»ç»èªå¨çæï¼`åºå®åç¼-Xå·/åç±»-YYMMDD-nnn`ï¼ï¼ç³»ç»ç®¡çæ°å¢âå·¥ç¨ä¸ä¸é
ç½®ââTéä¸ä¸é
ç½®âï¼Téä¸ä¸/å·¥ç¨ä¸ä¸ä¸ææ¹ç±é
置读åã |
| | | 17. AXU14 æä½è§è²ãåç¶æè®¾ç½®æé®çå¯ç¨æ¡ä»¶ï¼ä»¥å跳转 AXU15 æ¶æ¯å¦ä¼ å
¥éä¸ç派工åã |
| | | 18. å®å·¥éªæ¶æµç¨åç¯èæä½è§è²ï¼å®å·¥éªæ¶æå°è¾åºçå®é
æå°è¡ä¸ºï¼âä¿®åè´¨éè¯çº§âä¸ AXU14âTåè¯åâåæ®µçå
³ç³»ã |
| | | |
| New file |
| | |
| | | 1ãlogo ç¨publicç®å½ä¸çlogo.pngæä»¶ |
| | | 2 ãæ·»å ç¨æ·ç®¡çåè½ï¼åå·¦å³ç»æï¼å·¦è¾¹è¡¨æ ¼æ¾ç¤ºææç¨æ·ä¿¡æ¯ï¼ç¨æ·åï¼è´¦æ·åï¼æå±é¨é¨ï¼è§è²æéçä¿¡æ¯ï¼ï¼å¯è¿æ»¤ï¼å³è¾¹æ¾ç¤ºå¾
æ£æ¥æ¡çè§è²å表ï¼å¯ä»¥è®¾ç½®ååæ¾ç¨æ·æ¥æçè§è²ï¼ä¸ä¸ªç¨æ·å¯ä»¥è®¾ç½®å¤ä¸ªè§è² |
| | | 3 ãç³»ç»è®°å½ç¨æ·ç»å½æ¥å¿ãæä½æ¥å¿ï¼æä½æ¥å¿ä¸»è¦è®°å½æ·»å è®°å½æä¿®æ¹è®°å½çæ¥å¿ |
| | | 4ãèå管ççç±»åé¤äºç®å½åèåå¤ï¼å¢å ä¸ä¸ªæé®ç±»åï¼è¿ä¸ªåªè½å¨èåç±»åçä¸çº§ï¼ç¨äºè®¾ç½®ç¨æ·æ¥æä¸çº§èå页é¢çæé®æä½æéï¼æåªäºæé®æ¯å¦å¯ä»¥èªå¨è·å |
| | | |
| | | |
| | | |
| | | 5ã代ç ä¸çâè£
å¤âåæ ·æ¹æâ设å¤â |
| | | 6ãå·¥ä½åºçå¤ææ¡£åæ¢æ ç¾ï¼å½æå¼ææ¡£å¤çæ¶ååºç°æ¨ªåæ»å¨æ¡çæ¶åæ ç¾é«åº¦ä¸å¤ï¼æåæ¾ç¤ºä¸å
¨ |
| | | 7ãæ¾å¼æéå«çç¨æ·ä¹å¯ä»¥ç»å½ |
| | | 8ãèåæé管çï¼æ£æ¥æ¡ç¶åèç¹æ¹æèå¨ç |
| | | |
| | | 9ãç¨æ·ç®¡çå¢å æ¹é设置è§è²ï¼è§è²ç®¡çé£éå¢å è§è²ç¨æ·è®¾ç½® |
| | | 10ã产åç»ææ è¡¨æ¹æåä¹è¯è¡¨ZT_BOAT_TREEï¼labelæ¯è®¾å¤åç§°ï¼info_idæ¯è®¾å¤ä¸»é®idï¼CONNECT_IDæ¯ç¶èç¹idï¼CONNECT_GROUPæ¯ç¥è¾èç¹è·¯å¾ï¼LAYERæ¯èç¹ç±»åï¼modelãsideãsystem1ãsystem2ãequipmentçï¼ ä¸æ¯ä¸¥æ ¼æç
§è¿ä¸ªé¡ºåºæ¥çï¼ å¯è½æ²¡æsystem2å±ï¼system2䏿çå¯è½ä¸æ¯equipmentç±»åï¼å¯è½æ¯å«çç±»åï¼å头æåæ´çä¸ |
| | | |
| | | |
| | | 11ãå®å·¥æ
åµå¤çé£éï¼ç¶æä¿®æ¹å®å·¥çæ¶åï¼ä¸è¦è·³è½¬å°å®å·¥ä¿¡æ¯å¡«æ¥ï¼å ä¸ºä¸æ¯åä¸ä¸ªç¨æ·æä½ç |
| | | ç¹å®å·¥åªæ¯æç¶æå®å·¥ï¼å®å·¥ä¿¡æ¯å¡«æ¥æ¯ç±å«çç¨æ·å¡«åç |
| | | 12 ãå®å·¥ä¿¡æ¯å¡«æ¥é£éï¼æ´¾å·¥åéæ©å表ï¼ä¸æ¯å¨å®å·¥å¤çæ
åµé£éå·²ç»ç¡®å®å®å·¥ç¶æçï¼äºå°±æ¯è¦æ±åç»ç»å½ç¨æ·æå±å使´¾å·¥åï¼å¦å¤å¤æ¾ç¤ºä¸ä¸ªå·¥ç¨åç§°ï¼æé¡¹ç®åç§°ï¼ä¿¡æ¯ |
| | | 17ãè§è²ç®¡çç¨æ·è®¾ç½®å¢å ä¸ä¸ªå
¨é¨/å·²è®¾ç½®åæ¢åè½ |
| | | |
| | | 13ãsensitive_wordsçcodenameçå¼åé¢å "rk_"åç¼ï¼å¹¶ä¿®æ¹ç°å¨ä»£ç çç¸åºçææ¬ |
| | | 14ãçææ
éç¼å·ä¸çåºå®åç¼å¨åå°é
ç½®æä»¶ä¸é
ç½® |
| | | 15ãææè¡¨è®°å½å å建人ãä¿®æ¹äººï¼æ·»å ä¿®æ¹æ¶ç³»ç»èªå¨å¡«å |
| | | 16ãææè¡¨å é»è¾å é¤å段ï¼å é¤é½æ¯é»è¾å é¤ï¼æ¥è¯¢çæ¶åè¿æ»¤æé»è¾å é¤çåæ®µ |
| | | |
| | | 17ãè§è²ç®¡çç¨æ·è®¾ç½®å¢å ä¸ä¸ªå
¨é¨/å·²è®¾ç½®åæ¢åè½ |
| | | 18ãå¢å åè½æ¾ç¤ºç»ç»æºæï¼é¨é¨æ è¡¨ï¼ |
| | | 19ãæ¿ä¿®åä½åºè¯¥å°é¨é¨è¡¨éæ¾DEPT_TYPEå为cjçè®°å½ |
| | |
| | | { |
| | | "sensitive_words": [ |
| | | { |
| | | "codename": "Xå·", |
| | | "codename": "rk_Xå·", |
| | | "real_name": "è·å·", |
| | | "scope": "documents,ui,code", |
| | | "note": "è°è¹ç¼å·" |
| | | }, |
| | | { |
| | | "codename": "JC", |
| | | "codename": "rk_JC", |
| | | "real_name": "è°è¹", |
| | | "scope": "documents,ui,code", |
| | | "note": "å¦ JCä¿¡æ¯=è°è¹ä¿¡æ¯" |
| | | }, |
| | | { |
| | | "codename": "JCC", |
| | | "codename": "rk_JCC", |
| | | "real_name": "è°è¹å¤", |
| | | "scope": "documents,ui,code", |
| | | "note": "审æ¹è§è²ï¼JCCå©ç/JCCå¤é¿/JCCéªæ¶" |
| | | }, |
| | | { |
| | | "codename": "CMB", |
| | | "codename": "rk_CMB", |
| | | "real_name": "åè°é¨", |
| | | "scope": "documents,ui,code", |
| | | "note": "审æ¹è§è²ï¼CMBé¢å¯¼" |
| | | }, |
| | | { |
| | | "codename": "T", |
| | | "codename": "rk_T", |
| | | "real_name": "è", |
| | | "scope": "documents,ui,code", |
| | | "note": "è¯æ ¹ä»£ç§°ï¼ç»åè¯èªå¨æ¿æ¢ï¼Téä¸ä¸ãTå
æ½å·¥çï¼" |
| | | }, |
| | | { |
| | | "codename": "TD", |
| | | "codename": "rk_TD", |
| | | "real_name": "èé", |
| | | "scope": "documents,ui,code", |
| | | "note": "å¦é¦é¡µç»è®¡è¡¨TDåï¼Xå·æå±èéï¼" |
| | | }, |
| | | { |
| | | "codename": "XCB", |
| | | "codename": "rk_XCB", |
| | | "real_name": "ä¿®è¹å", |
| | | "scope": "documents,ui,code", |
| | | "note": "å大éå©çå®¡æ ¸è§è²ï¼ç°é¡µé¢åèåç»ä¸ç§°XCBå©çå®¡æ ¸" |
| | | }, |
| | | { |
| | | "codename": "H", |
| | | "codename": "rk_H", |
| | | "real_name": "[production-only sensitive value]", |
| | | "scope": "documents,ui,code", |
| | | "note": "AXU3 ååç»åæ®µâæ¯å¦æ¶Hâï¼å¼åç¯å¢åªè¾åºä»£ç§°" |
| | |
| | | </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 å
¶ä»ä¸å¡æé®æä¸éè¦ãæ
éç¼å·ç±ç³»ç»æ `æ½1åº-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 已解å³ï¼ï¼âæ¥ä»·ç®¡çâèå䏿¨¡åæ´ä½ä¸åï¼æ¥ä»·å¤çç§»åºä¸»æµç¨ï¼ï¼èé¿ç¯èä¸çº³å
¥ç¬¬ä¸çï¼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> |
| | |
| | | <div class="note2">â ä¸åå
å«âèé¿æ¹é审æ¹/èé¿å®¡æ¹âï¼ç´æ¥ä» 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> |
| | | </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>æ½1åº-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> |
| | | </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> |
| | | <div class="note2">æä½ï¼å¾éä¸ä¸ªæå¤ä¸ªæ
éï¼ç¹å»âä¸é®æ´¾åâãé¤âä¸é®æ´¾åâå¤ï¼åæªå¾ä¸çä¿®æ¹ãçæä¸´ä¿®å·¥ç¨åãå é¤äºå¤ï¼ç为æ¸
é¤éå¤ï¼çä¸å¡æé®æä¸éè¦ã</div> |
| | |
| | | <div class="chips"><span class="td">ä¸é®æ´¾åï¼åOCRâ鮿´¾åâï¼</span><span class="td"><mark>ä¿®æ¹ãçæä¸´ä¿®å·¥ç¨åãå é¤äºå¤ï¼çä¸ºâæ¸
é¤éå¤âï¼æä¸éè¦</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">Xå·</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-28 æ´æ°ï¼ï¼ç±ç³»ç»èªå¨çæï¼æ½1åº-Xå·/åç±»-YYMMDD-nnnï¼ï¼æ´¾å·¥åç¼å·ä¸ºâæ
éç¼å·/01ã/02â¦âã第ä¸çå®é
页é¢ä»¥âæªæ´¾åæ
éå表 + å¾é + ä¸é®æ´¾åâ为åã</div> |
| | | <div class="note2">æ
éç¼å·æ¥æºï¼2026-09-28 æ´æ°ï¼ï¼ç±ç³»ç»èªå¨çæï¼åºå®åç¼-Xå·/åç±»-YYMMDD-nnnï¼ï¼æ´¾å·¥åç¼å·ä¸ºâæ
éç¼å·/01ã/02â¦âã第ä¸çå®é
页é¢ä»¥âæªæ´¾åæ
éå表 + å¾é + ä¸é®æ´¾åâ为åã</div> |
| | | </section><section class="card"><h3>AXU11 · 已派工ç¨ï¼ææ¡£é¡µ 17ï¼</h3><div class="note"><mark>æä¸å¤ç 2026-09-20ï¼ä¸çº³å
¥å½åéæ±ä¸åç»æ ¸å¯¹ã</mark></div><div class="note">æ¤éç管ç</div><div class="label">æé®/æä½</div><div class="chips"><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><span class="td">æ¤é</span><span class="td">详æ
</span></div><div class="note2">è¡æä½ï¼æ¤éæ´¾å / æ¥ç详æ
</div></section><section class="card"><h3>AXU12 · 已派工ç¨åï¼ææ¡£é¡µ 18ï¼</h3><div class="note"><mark>æä¸å¤ç 2026-09-20ï¼ä¸çº³å
¥å½åéæ±ä¸åç»æ ¸å¯¹ã</mark></div><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">å¾
å¤ä»¶</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><span class="td">æ´¾ååä½</span><span class="td">Xå·</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>AXU13 · å·¥ç¨åæå°è¾åºï¼ææ¡£é¡µ 19ï¼</h3><div class="note"><mark>æä¸å¤ç 2026-09-20ï¼ä¸çº³å
¥å½åéæ±ä¸åç»æ ¸å¯¹ã</mark></div><div class="note">页é¢å¤æ³¨ï¼</div><div class="note2">1. æ¹éæå°è¾åºï¼è¾åºç®å项ç®ä¿¡æ¯ï¼ä¾æ¥ä»·ä½¿ç¨</div><div class="note2">2. 忡坼åºä¹å¯è½æå¤æ¡ä¿¡æ¯ï¼è¿éæ¯ææ´¾å·¥åå表çè¡¨æ ¼</div><div class="label">æé®/æä½</div><div class="chips"><span class="td">å·æ°</span><span class="td">æ¹é导åºå®å·¥éªæ¶å</span><span class="td">æ¹é导åºå®å·¥å</span><span class="td">导åºå¾
å®å·¥æ°æ®å
<mark>2026-09-14ï¼å·²ç¡®è®¤åæ¶ï¼ç¦»çº¿æ°æ®å
ï¼C-12ï¼</mark></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><span class="td">ä¿®è¹åå®¡æ ¸æ¶é´</span><span class="td">æ´¾ååä½</span><span class="td">Xå·</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">æä½ï¼æ¥ç / 导åº</span></div></section><section class="card"><h3>AXU14 · å®å·¥æ
åµå¤çï¼ææ¡£é¡µ 20ï¼2026-09-20 æç¨æ·å£å¾éå®ä¹ï¼</h3> |
| | | <div class="note2">页é¢ä½ç½®ï¼æ´¾ååè¿å
¥æ¬é¡µãæ¯ä¸è¡ä»£è¡¨1个派工åï¼åªå
许å¾é1è¡ã</div> |
| | | <div class="note2">åè¡¨åæ®µè°æ´ï¼å»æâå·¥ç¨ååå·âï¼å¢å âå®å·¥ç¶æâãæ´¾ç工忶ï¼å®å·¥ç¶æé»è®¤å¼ä¸ºâæªå®å·¥âã</div> |