feat: add completion query and configurable system identity
- add fault-grouped completion query with merged detail cells
- make system name and version keyword-configurable
- allow editing menu parent with cycle validation
- update handoff records for requirements 23-25
| | |
| | | - é误æ¥å¿å½åè¦çè¿å
¥ Spring MVC å
¨å±å¼å¸¸å¤ççå¼å¸¸ï¼å¯å¨é¶æ®µå¼å¸¸ã弿¥çº¿ç¨ä¸æªè¿å
¥è¯¥å¤çå¨çå¼å¸¸æä¸ä¼èªå¨è½åºã |
| | | - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã |
| | | - æ¬è½®ä»£ç ä¸äº¤æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | |
| | | ## 20. æ¶å°¾å¿«ç
§ï¼2026-10-06 å¨ä¸ï¼éæ± 23-25 ç³»ç»é
ç½®ãå®å·¥æ¥é
ä¸èåç¶èç¹ï¼ |
| | | |
| | | **æ¬è½®å®æå
容** |
| | | - éæ± 23ï¼ç³»ç»åç§°åçæ¬æ¹ä¸ºå
³é®è¯é
ç½®ï¼codename åå«ä¸º `rk_SystemName`ã`rk_SystemVer`ãç»å½é¡µã页头ãLogo æ¿ä»£ææ¬åæµè§å¨æ é¢ä½¿ç¨é
ç½®å¼ï¼é¡µå¤´çæ¬ä½¿ç¨çº¢è²æ¾ç¤ºã |
| | | - éæ± 24ï¼æ°å¢âå®å·¥ä¿¡æ¯æ¥é
âåè½ï¼èå `/completion/query`ï¼æ¥å£ `GET /api/work-orders/completion/query`ãæ¥è¯¢ç»æææ
éæ±æ»ï¼ä¸ä¸ªæ
éå±å¼å¤å¼ 派工åï¼æ
éç¼å·ãrk_Xå·ã设å¤ãå·¥ç¨åç§°åæåæ
åµã䏿¥åä½ã䏿¥æ¥æãåºå·è¿ 7 å使ç¨åå
æ ¼çºµååå¹¶ãæ¯å¼ æ´¾å·¥åçå®å·¥å¤çæ
åµåæ´æ¢å¨ææ¯æå¤æ¡æç»ã |
| | | - éæ± 24 çæ¥è¯¢æ¡ä»¶å
æ¬å
³é®è¯ãæ¿ä¿®åä½ãå®å·¥ç¶æåå®å·¥æ¥æãå
³é®è¯å¹é
æ
éãæ´¾å·¥åãå·¥ç¨ç¼å·ãæ¿ä¿®äººåæç»å
容ã |
| | | - éæ± 25ï¼èå管çç¼è¾æ¶ï¼âä¸çº§èåâæ¹ä¸ºå¯ç¼è¾çº§èéæ©å¨ï¼ç¼è¾æ¶æé¤å½åèååå
¶å
¨é¨ä¸çº§ï¼æé®ç±»ååªè½éæ©èåç±»åç¶èç¹ã |
| | | - éæ± 25 åç«¯åæ¥å¢å ç¶èç¹å卿§ãèªå¼ç¨å循ç¯å±çº§æ ¡éªï¼é¿å
éè¿æ¥å£æé 循ç¯èåæ ã |
| | | |
| | | **ä¸»è¦æä»¶** |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/CompletionFaultQueryItem.java`ï¼æ°å¢ï¼ |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/controller/WorkOrderController.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-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java` |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/completion/CompletionQuery.vue`ï¼æ°å¢ï¼ |
| | | - `emergency-repair-web/src/views/system/MenuManage.vue` |
| | | - `emergency-repair-web/src/components/AppHeader.vue`ã`Login.vue`ã`RepairLogo.vue`ã`layout/AppLayout.vue`ã`router/index.js`ã`utils/sensitive.js`ã`main.js` |
| | | - `emergency-repair-web/public/sensitive-words.json`ã`public/index.html` |
| | | - `requirement/ææè¯åå
¸.json` |
| | | |
| | | **éªè¯ç»æ** |
| | | - å端 `mvn -B -DskipTests compile` éè¿ï¼å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req23-25.js`ï¼é¡µé¢æ é¢å页头使ç¨â临æ¢ä¿®ç®¡çç³»ç» / ç½ç»çâï¼çæ¬é¢è²ä¸º `rgb(255, 107, 107)`ï¼å®å·¥æ¥é
æ¾ç¤ºæ
é `GZ-2026-012`ãå¤çæ
åµå卿ï¼èåç¼è¾ç¶èç¹æ¾ç¤ºâæ
é管çâä¸å¯ç¼è¾ã |
| | | - æ¥å£å®æµï¼ä¸´æ¶å䏿
éæå
¥ä¸¤å¼ 派工åå两æ¡å®å·¥è®°å½åï¼`GET /api/work-orders/completion/query?keyword=ZZ-MERGE-TEST-20261006` è¿å 1 个æ
éã2 å¼ æ´¾å·¥åï¼æ¯æ¡å 1 æ¡å¤çæ
åµå 1 æ¡å¨æã |
| | | - æµè§å¨å并宿µï¼é¡µé¢çæ 2 å¼ æ´¾å·¥åè¡ï¼æ
éç¼å·åå
æ ¼åªåºç° 1 次ï¼7 个æ
éä¿¡æ¯ååæ `rowspan=2`ï¼ä¸¤å¼ ååèªçå¤çæ
åµå卿忣叏æ¾ç¤ºï¼æ ç½ç»å¼å¸¸ï¼æªå¾ `.codex-run/completion-query-merge-verified.png`ãä¸´æ¶æ°æ®åºè®°å½å·²æ¸
çã |
| | | - èåç¶èç¹æ¥å£å®æµï¼ä¸´æ¶å建ç¶åèåæåï¼æç¶èåç§»å¨å°å
¶åèåä¸è¿åâä¸çº§èåä¸è½éæ©å½åèåçä¸çº§âï¼æåèåç§»å¨å°å
¶ä»ç¶èç¹æåï¼ä¸´æ¶èåå·²æ¸
çã |
| | | |
| | | **å½åè¿è¡ç¶æï¼2026-10-06ï¼** |
| | | - å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | - å端æ¥å¿ï¼`.codex-run/backend-20261006-req23-25-v2.out.log`ã`.codex-run/backend-20261006-req23-25-v2.err.log`ã |
| | | |
| | | **æ¥ææ³¨æ** |
| | | - å®å·¥ä¿¡æ¯æ¥é
å½ååªæ±æ»å·²ç»åå¨å®å·¥å¡«æ¥è®°å½ `T_COMPLETION_INFO` çæ´¾å·¥åï¼ä»
å®å·¥å¤ç使ªå¡«æ¥å®å·¥ä¿¡æ¯ç派工åä¸ä¼åºç°å¨æ¥é
ç»æä¸ã |
| | | - å®å·¥æ¥é
æ¥å£å½å䏿¬¡è¿åå
¨é¨å¹é
ç»æï¼æ²¡æåé¡µï¼æ°æ®éè¿ä¸æ¥å¢å¤§æ¶å»ºè®®å¢å å页æå¯¼åºçç¥ã |
| | | - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | |
| | | public void run(String... args) { |
| | | seedMenusAndRoles(); |
| | | ensureSystemMenus(); |
| | | ensureCompletionMenus(); |
| | | normalizeSensitiveTerms(); |
| | | normalizeEquipmentTerms(); |
| | | seedButtonMenus(); |
| | |
| | | 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("completion-query", fault, "å®å·¥ä¿¡æ¯æ¥é
", "/completion/query", null, 1, 90); |
| | | |
| | | Long acceptance = createMenu("acceptance", null, "å®å·¥éªæ¶", null, "el-icon-circle-check", 0, 30); |
| | | createMenu("accept-application", acceptance, "éªæ¶ç³è¯·", "/acceptance/application", null, 1, 10); |
| | |
| | | return menu.getId(); |
| | | } |
| | | |
| | | private void ensureCompletionMenus() { |
| | | SysMenu fault = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getName, "æ
é管ç") |
| | | .eq(SysMenu::getMenuType, 0) |
| | | .last("LIMIT 1")); |
| | | Long pid = fault == null ? 0L : fault.getId(); |
| | | ensureMenu(pid, "å®å·¥ä¿¡æ¯æ¥é
", "/completion/query", 90); |
| | | grantAdminMenus("/completion/query"); |
| | | } |
| | | |
| | | private void ensureSystemMenus() { |
| | | SysMenu system = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getName, "\u7cfb\u7edf\u7ba1\u7406").last("LIMIT 1")); |
| | |
| | | package com.emergencyrepair.business.controller; |
| | | |
| | | import com.emergencyrepair.business.dto.Requests; |
| | | import com.emergencyrepair.business.dto.CompletionFaultQueryItem; |
| | | import com.emergencyrepair.business.entity.CompletionInfo; |
| | | import com.emergencyrepair.business.entity.WorkOrder; |
| | | import com.emergencyrepair.business.service.BusinessService; |
| | | import com.emergencyrepair.common.Result; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.List; |
| | | |
| | | @RestController |
| | |
| | | return Result.ok(businessService.listWorkOrders(status, keyword, completionReportable)); |
| | | } |
| | | |
| | | @GetMapping("/completion/query") |
| | | public Result<List<CompletionFaultQueryItem>> completionQuery( |
| | | @RequestParam(required = false) String keyword, |
| | | @RequestParam(required = false) String completionStatus, |
| | | @RequestParam(required = false) String contractorName, |
| | | @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, |
| | | @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { |
| | | return Result.ok(businessService.listCompletionQueries(keyword, completionStatus, contractorName, startDate, endDate)); |
| | | } |
| | | |
| | | @GetMapping("/{id}") |
| | | public Result<WorkOrder> detail(@PathVariable Long id) { |
| | | return Result.ok(businessService.getWorkOrder(id)); |
| New file |
| | |
| | | package com.emergencyrepair.business.dto; |
| | | |
| | | import lombok.Data; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class CompletionFaultQueryItem { |
| | | private String faultId; |
| | | private String faultCode; |
| | | @JsonProperty("xNo") |
| | | private String xNo; |
| | | private String equipmentName; |
| | | private String parentSystem; |
| | | private String engineeringMajor; |
| | | private String teamMajor; |
| | | private String reportUnit; |
| | | private LocalDate reportDate; |
| | | private String owner; |
| | | private String engineeringNameDesc; |
| | | private String taskStatus; |
| | | private List<WorkOrderCompletionItem> workOrders = new ArrayList<>(); |
| | | |
| | | @Data |
| | | public static class WorkOrderCompletionItem { |
| | | private String workOrderId; |
| | | private String workOrderNo; |
| | | private String contractorName; |
| | | private String completionStatus; |
| | | private String engineeringNo; |
| | | private LocalDate startDate; |
| | | private LocalDate finishDate; |
| | | private String contractorPerson; |
| | | @JsonProperty("tInternalConstruction") |
| | | private String tInternalConstruction; |
| | | private String relatedProject; |
| | | private Integer durationDays; |
| | | private Integer personCount; |
| | | private String acceptanceMode; |
| | | private String qualityInfo; |
| | | private String reasonAnalysis; |
| | | private String faultDescription; |
| | | private String itemName; |
| | | private LocalDateTime submitTime; |
| | | private List<Requests.SolutionItem> solutions = new ArrayList<>(); |
| | | private List<Requests.MaterialItem> materials = new ArrayList<>(); |
| | | } |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.emergencyrepair.business.dto.Requests; |
| | | import com.emergencyrepair.business.dto.CompletionFaultQueryItem; |
| | | import com.emergencyrepair.business.entity.Acceptance; |
| | | import com.emergencyrepair.business.entity.CompletionInfo; |
| | | import com.emergencyrepair.business.entity.Fault; |
| | |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Collection; |
| | | import java.util.Comparator; |
| | | import java.util.Objects; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | |
| | | return created; |
| | | } |
| | | |
| | | public List<CompletionFaultQueryItem> listCompletionQueries(String keyword, String completionStatus, |
| | | String contractorName, LocalDate startDate, |
| | | LocalDate endDate) { |
| | | List<CompletionInfo> completions = completionInfoMapper.selectList(Wrappers.<CompletionInfo>lambdaQuery() |
| | | .orderByDesc(CompletionInfo::getSubmitTime).orderByDesc(CompletionInfo::getUpdateTime)); |
| | | if (completions.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | Set<Long> workOrderIds = completions.stream().map(CompletionInfo::getWorkOrderId) |
| | | .filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | if (workOrderIds.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<Long, WorkOrder> workOrderMap = workOrderMapper.selectBatchIds(workOrderIds).stream() |
| | | .collect(Collectors.toMap(WorkOrder::getId, item -> item)); |
| | | Set<Long> faultIds = workOrderMap.values().stream().map(WorkOrder::getFaultId) |
| | | .filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | if (faultIds.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<Long, Fault> faultMap = faultMapper.selectBatchIds(faultIds).stream() |
| | | .collect(Collectors.toMap(Fault::getId, item -> item)); |
| | | |
| | | String normalizedKeyword = StringUtils.hasText(keyword) ? keyword.trim().toLowerCase() : null; |
| | | String normalizedContractor = StringUtils.hasText(contractorName) ? contractorName.trim().toLowerCase() : null; |
| | | Map<Long, CompletionFaultQueryItem> grouped = new LinkedHashMap<>(); |
| | | for (CompletionInfo completion : completions) { |
| | | WorkOrder workOrder = workOrderMap.get(completion.getWorkOrderId()); |
| | | if (workOrder == null) { |
| | | continue; |
| | | } |
| | | if (StringUtils.hasText(completionStatus) |
| | | && !completionStatus.equalsIgnoreCase(workOrder.getCompletionStatus())) { |
| | | continue; |
| | | } |
| | | if (normalizedContractor != null && !containsIgnoreCase(workOrder.getContractorName(), normalizedContractor)) { |
| | | continue; |
| | | } |
| | | LocalDate completionDate = completion.getFinishDate() != null |
| | | ? completion.getFinishDate() |
| | | : (completion.getSubmitTime() == null ? null : completion.getSubmitTime().toLocalDate()); |
| | | if (startDate != null && (completionDate == null || completionDate.isBefore(startDate))) { |
| | | continue; |
| | | } |
| | | if (endDate != null && (completionDate == null || completionDate.isAfter(endDate))) { |
| | | continue; |
| | | } |
| | | |
| | | Fault fault = faultMap.get(workOrder.getFaultId()); |
| | | if (fault == null) { |
| | | continue; |
| | | } |
| | | CompletionFaultQueryItem.WorkOrderCompletionItem workOrderItem = buildCompletionWorkOrderItem(workOrder, completion); |
| | | if (normalizedKeyword != null && !matchesCompletionKeyword(fault, workOrderItem, normalizedKeyword)) { |
| | | continue; |
| | | } |
| | | |
| | | CompletionFaultQueryItem item = grouped.get(fault.getId()); |
| | | if (item == null) { |
| | | item = new CompletionFaultQueryItem(); |
| | | item.setFaultId(String.valueOf(fault.getId())); |
| | | item.setFaultCode(fault.getFaultCode()); |
| | | item.setXNo(fault.getXNo()); |
| | | item.setEquipmentName(fault.getEquipmentName()); |
| | | item.setParentSystem(fault.getParentSystem()); |
| | | item.setEngineeringMajor(fault.getEngineeringMajor()); |
| | | item.setTeamMajor(fault.getTeamMajor()); |
| | | item.setReportUnit(fault.getReportUnit()); |
| | | item.setReportDate(fault.getReportDate()); |
| | | item.setOwner(fault.getOwner()); |
| | | item.setEngineeringNameDesc(fault.getEngineeringNameDesc()); |
| | | item.setTaskStatus(fault.getTaskStatus()); |
| | | grouped.put(fault.getId(), item); |
| | | } |
| | | item.getWorkOrders().add(workOrderItem); |
| | | } |
| | | |
| | | List<CompletionFaultQueryItem> result = new ArrayList<>(grouped.values()); |
| | | result.forEach(item -> item.getWorkOrders().sort(Comparator.comparing( |
| | | CompletionFaultQueryItem.WorkOrderCompletionItem::getWorkOrderNo, |
| | | Comparator.nullsLast(String::compareTo)))); |
| | | result.sort(Comparator.comparing(CompletionFaultQueryItem::getReportDate, |
| | | Comparator.nullsLast(Comparator.reverseOrder()))); |
| | | return result; |
| | | } |
| | | |
| | | private CompletionFaultQueryItem.WorkOrderCompletionItem buildCompletionWorkOrderItem( |
| | | WorkOrder workOrder, CompletionInfo completion) { |
| | | CompletionFaultQueryItem.WorkOrderCompletionItem item = new CompletionFaultQueryItem.WorkOrderCompletionItem(); |
| | | item.setWorkOrderId(String.valueOf(workOrder.getId())); |
| | | item.setWorkOrderNo(workOrder.getWorkOrderNo()); |
| | | item.setContractorName(workOrder.getContractorName()); |
| | | item.setCompletionStatus(workOrder.getCompletionStatus()); |
| | | item.setEngineeringNo(completion.getEngineeringNo()); |
| | | item.setStartDate(completion.getStartDate()); |
| | | item.setFinishDate(completion.getFinishDate()); |
| | | item.setContractorPerson(completion.getContractorPerson()); |
| | | item.setTInternalConstruction(completion.getTInternalConstruction()); |
| | | item.setRelatedProject(completion.getRelatedProject()); |
| | | item.setDurationDays(completion.getDurationDays()); |
| | | item.setPersonCount(completion.getPersonCount()); |
| | | item.setAcceptanceMode(completion.getAcceptanceMode()); |
| | | item.setQualityInfo(completion.getQualityInfo()); |
| | | item.setReasonAnalysis(completion.getReasonAnalysis()); |
| | | item.setFaultDescription(completion.getFaultDescription()); |
| | | item.setItemName(completion.getItemName()); |
| | | item.setSubmitTime(completion.getSubmitTime()); |
| | | List<Requests.SolutionItem> solutions = parseJsonList(completion.getSolutionsJson(), Requests.SolutionItem.class); |
| | | if (solutions.isEmpty() && StringUtils.hasText(completion.getSolution())) { |
| | | Requests.SolutionItem legacy = new Requests.SolutionItem(); |
| | | legacy.setContent(completion.getSolution()); |
| | | solutions.add(legacy); |
| | | } |
| | | item.setSolutions(solutions); |
| | | item.setMaterials(parseJsonList(completion.getConsumablesJson(), Requests.MaterialItem.class)); |
| | | return item; |
| | | } |
| | | |
| | | private boolean matchesCompletionKeyword(Fault fault, CompletionFaultQueryItem.WorkOrderCompletionItem item, |
| | | String keyword) { |
| | | if (containsIgnoreCase(fault.getFaultCode(), keyword) |
| | | || containsIgnoreCase(fault.getXNo(), keyword) |
| | | || containsIgnoreCase(fault.getEquipmentName(), keyword) |
| | | || containsIgnoreCase(fault.getEngineeringNameDesc(), keyword) |
| | | || containsIgnoreCase(fault.getReportUnit(), keyword) |
| | | || containsIgnoreCase(item.getWorkOrderNo(), keyword) |
| | | || containsIgnoreCase(item.getContractorName(), keyword) |
| | | || containsIgnoreCase(item.getEngineeringNo(), keyword) |
| | | || containsIgnoreCase(item.getItemName(), keyword) |
| | | || containsIgnoreCase(item.getContractorPerson(), keyword)) { |
| | | return true; |
| | | } |
| | | return item.getSolutions().stream().anyMatch(solution -> containsIgnoreCase(solution.getContent(), keyword)) |
| | | || item.getMaterials().stream().anyMatch(material -> containsIgnoreCase(material.getName(), keyword) |
| | | || containsIgnoreCase(material.getSpecification(), keyword)); |
| | | } |
| | | |
| | | private boolean containsIgnoreCase(String value, String keyword) { |
| | | return StringUtils.hasText(value) && value.toLowerCase().contains(keyword); |
| | | } |
| | | |
| | | private <T> List<T> parseJsonList(String json, Class<T> type) { |
| | | if (!StringUtils.hasText(json)) { |
| | | return new ArrayList<>(); |
| | | } |
| | | try { |
| | | return objectMapper.readValue(json, objectMapper.getTypeFactory().constructCollectionType(List.class, type)); |
| | | } catch (Exception exception) { |
| | | return new ArrayList<>(); |
| | | } |
| | | } |
| | | |
| | | public List<WorkOrder> listWorkOrders(String status, String keyword, boolean completionReportable) { |
| | | LambdaQueryWrapper<WorkOrder> query = Wrappers.lambdaQuery(); |
| | | if (completionReportable) { |
| | |
| | | put("/completion/report", |
| | | def("completion:report:save", "\u4fdd\u5b58"), |
| | | def("completion:report:submit", "\u63d0\u4ea4")); |
| | | put("/completion/query", |
| | | def("completion:query:query", "\u67e5\u8be2")); |
| | | put("/acceptance/application", |
| | | def("acceptance:application:query", "\u67e5\u8be2"), |
| | | def("acceptance:application:apply", "\u7533\u8bf7")); |
| | |
| | | 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("æé®ç±»ååªè½å»ºå¨èåç±»åä¸çº§"); |
| | | if (menu.getId() != null && !isRoot && menu.getId().equals(menu.getPid())) { |
| | | throw new BusinessException("ä¸çº§èåä¸è½éæ©å½åèå"); |
| | | } |
| | | SysMenu parent = menuMapper.selectById(menu.getPid()); |
| | | SysMenu parent = null; |
| | | if (!isRoot) { |
| | | parent = menuMapper.selectById(menu.getPid()); |
| | | if (parent == null) { |
| | | throw new BusinessException("ä¸çº§èåä¸åå¨"); |
| | | } |
| | | if (menu.getId() != null && isMenuDescendant(menu.getPid(), menu.getId())) { |
| | | throw new BusinessException("ä¸çº§èåä¸è½éæ©å½åèåçä¸çº§"); |
| | | } |
| | | } |
| | | if (menu.getMenuType() == 2) { |
| | | if (parent == null || parent.getMenuType() == null || parent.getMenuType() != 1) { |
| | | 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) { |
| | | } else if (parent != null && parent.getMenuType() != null && parent.getMenuType() == 2) { |
| | | throw new BusinessException("æé®ç±»åä¸çº§ä¸è½å建èå"); |
| | | } |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | menu.setUpdateTime(now); |
| | |
| | | return menuMapper.selectById(menu.getId()); |
| | | } |
| | | |
| | | private boolean isMenuDescendant(Long candidateId, Long ancestorId) { |
| | | Long cursor = candidateId; |
| | | int guard = 0; |
| | | while (cursor != null && cursor != 0L && guard++ < 64) { |
| | | if (cursor.equals(ancestorId)) { |
| | | return true; |
| | | } |
| | | SysMenu current = menuMapper.selectById(cursor); |
| | | cursor = current == null ? null : current.getPid(); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | @Transactional |
| | | public void deleteMenu(Long id) { |
| | | long children = menuMapper.selectCount(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getPid, id)); |
| | |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <meta name="viewport" content="width=device-width,initial-scale=1"> |
| | | <title>临æ¢ä¿®ç®¡çç³»ç»</title> |
| | | <title>rk_SystemName</title> |
| | | </head> |
| | | <body> |
| | | <noscript>请å¯ç¨ JavaScript å使ç¨ä¸´æ¢ä¿®ç®¡çç³»ç»ã</noscript> |
| | |
| | | { |
| | | "sensitive_words": [ |
| | | { |
| | | "codename": "rk_SystemName", |
| | | "real_name": "临æ¢ä¿®ç®¡çç³»ç»", |
| | | "scope": "ui", |
| | | "note": "System title shown in the login page and application header." |
| | | }, |
| | | { |
| | | "codename": "rk_SystemVer", |
| | | "real_name": "ç½ç»ç", |
| | | "scope": "ui", |
| | | "note": "System edition shown in red in the application header." |
| | | }, |
| | | { |
| | | "codename": "rk_Xå·", |
| | | "real_name": "å¿å·", |
| | | "scope": "ui", |
| | |
| | | <header class="app-header"> |
| | | <div class="brand"> |
| | | <repair-logo></repair-logo> |
| | | <span>临æ¢ä¿®ç®¡çç³»ç»</span> |
| | | <em>ç½ç»ç</em> |
| | | <span>rk_SystemName</span> |
| | | <em class="system-version">rk_SystemVer</em> |
| | | <span v-if="menuPathText" class="menu-path" :title="menuPathText">{{ menuPathText }}</span> |
| | | </div> |
| | | <div class="header-right"> |
| | |
| | | .brand { display: flex; flex: 1; align-items: center; min-width: 0; gap: 9px; font-size: 18px; font-weight: 600; } |
| | | .brand .repair-logo { color: #7fc4ff; font-size: 25px; transform: translateY(2px); } |
| | | .brand em { padding: 1px 6px; border: 1px solid rgba(255,255,255,.35); border-radius: 3px; font-size: 11px; font-style: normal; font-weight: 400; opacity: .85; } |
| | | .brand .system-version { border-color: rgba(255, 107, 107, .65); color: #ff6b6b; opacity: 1; } |
| | | .brand > .repair-logo, .brand > span:not(.menu-path), .brand > em { flex: 0 0 auto; } |
| | | .menu-path { flex: 0 1 auto; min-width: 0; padding-left: 14px; border-left: 1px solid rgba(255,255,255,.28); overflow: hidden; color: #d8e9f7; font-size: 13px; font-weight: 400; text-overflow: ellipsis; white-space: nowrap; } |
| | | .header-right { display: flex; flex: 0 0 auto; align-items: center; gap: 14px; font-size: 14px; } |
| | |
| | | <template> |
| | | <img class="repair-logo" :src="logoSrc" alt="临æ¢ä¿®ç®¡çç³»ç»"> |
| | | <img class="repair-logo" :src="logoSrc" alt="rk_SystemName"> |
| | | </template> |
| | | |
| | | <script> |
| | |
| | | if (path === '/fault/report') return 'el-icon-edit-outline' |
| | | if (path.includes('/approval/') || path === '/fault/review') return 'el-icon-s-check' |
| | | if (path === '/fault/dispatch') return 'el-icon-s-promotion' |
| | | if (path.startsWith('/fault/completion') || path === '/completion/report') return 'el-icon-finished' |
| | | if (path.startsWith('/fault/completion') || path.startsWith('/completion/')) return 'el-icon-finished' |
| | | if (path.startsWith('/acceptance/')) return 'el-icon-circle-check' |
| | | if (path.startsWith('/reports/')) return 'el-icon-data-analysis' |
| | | if (path.startsWith('/system/')) return 'el-icon-setting' |
| | |
| | | Vue.use(ElementUI, { size: 'small' }) |
| | | Vue.config.productionTip = false |
| | | |
| | | Vue.prototype.$appName = '临æ¢ä¿®ç®¡çç³»ç»' |
| | | Vue.prototype.$appName = 'rk_SystemName' |
| | | |
| | | Vue.prototype.$hasPermission = function(code) { |
| | | const raw = sessionStorage.getItem('permissions') |
| | |
| | | { 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: 'completion/query', component: () => import('@/views/completion/CompletionQuery.vue'), meta: { title: 'å®å·¥ä¿¡æ¯æ¥é
', cacheName: 'CompletionQuery' } }, |
| | | { path: 'acceptance/application', component: () => import('@/views/acceptance/AcceptanceList.vue'), props: { stage: 'APPLICATION' }, meta: { title: 'éªæ¶ç³è¯·', stage: 'APPLICATION', cacheName: 'AcceptanceList' } }, |
| | | { path: 'acceptance/inspector', component: () => import('@/views/acceptance/AcceptanceList.vue'), props: { stage: 'INSPECTOR' }, meta: { title: 'è´¨æ£åéªæ¶', stage: 'INSPECTOR', cacheName: 'AcceptanceList' } }, |
| | | { path: 'acceptance/leader', component: () => import('@/views/acceptance/AcceptanceList.vue'), props: { stage: 'LEADER' }, meta: { title: 'è´¨æ£è´è´£äººéªæ¶', stage: 'LEADER', cacheName: 'AcceptanceList' } }, |
| | |
| | | }) |
| | | |
| | | replaceDom(document.body) |
| | | document.title = replaceSensitiveText(document.title) |
| | | watchDom() |
| | | } catch (error) { |
| | | // The application still works with development codenames if the dictionary is unavailable. |
| | |
| | | <div class="login-brand"> |
| | | <repair-logo></repair-logo> |
| | | <div> |
| | | <h1>临æ¢ä¿®ç®¡çç³»ç»</h1> |
| | | <h1>{{ $appName }}</h1> |
| | | </div> |
| | | </div> |
| | | <el-form ref="form" :model="form" :rules="rules" label-position="top" @keyup.enter.native="submit"> |
| New file |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>å®å·¥ä¿¡æ¯æ¥é
</h2> |
| | | <p>ææ
éæ±æ»ï¼å䏿
éçå¤å¼ 派工åå
±ç¨æ
éä¿¡æ¯ï¼å®å·¥å¤çæ
åµåæ´æ¢å¨ææå¤æ¡æç»å±ç¤º</p> |
| | | </div> |
| | | <el-tag type="info">å
± {{ rows.length }} 个æ
é</el-tag> |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | | <el-input v-model="filters.keyword" placeholder="æ
éç¼å· / rk_Xå· / 派工åç¼å· / å·¥ç¨åç§° / æ¿ä¿®åä½" clearable style="width:380px" @keyup.enter.native="load"></el-input> |
| | | <el-input v-model="filters.contractorName" placeholder="æ¿ä¿®åä½" clearable style="width:190px" @keyup.enter.native="load"></el-input> |
| | | <el-select v-model="filters.completionStatus" placeholder="å®å·¥ç¶æ" clearable style="width:140px"> |
| | | <el-option v-for="(meta, key) in completionStatuses" :key="key" :label="meta.text" :value="key"></el-option> |
| | | </el-select> |
| | | <el-date-picker v-model="filters.completionDate" type="daterange" value-format="yyyy-MM-dd" range-separator="è³" start-placeholder="å®å·¥å¼å§æ¥æ" end-placeholder="å®å·¥ç»ææ¥æ" style="width:270px"></el-date-picker> |
| | | <el-button type="primary" icon="el-icon-search" v-permission="'completion:query:query'" @click="load">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh" @click="reset">éç½®</el-button> |
| | | </div> |
| | | |
| | | <div class="table-panel"> |
| | | <el-table v-loading="loading" :data="displayRows" border size="small" :span-method="spanMethod" row-key="rowKey"> |
| | | <el-table-column label="åºå·" width="62" align="center"> |
| | | <template slot-scope="{ row }">{{ row.faultIndex }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="faultCode" label="æ
éç¼å·" width="150"></el-table-column> |
| | | <el-table-column prop="xNo" label="rk_Xå·" width="72"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="设å¤åç§°" width="130" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="engineeringNameDesc" label="å·¥ç¨åç§°åæåæ
åµ" min-width="190" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="reportUnit" label="䏿¥åä½" width="130" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="reportDate" label="䏿¥æ¥æ" width="105"></el-table-column> |
| | | |
| | | <el-table-column prop="workOrderNo" label="派工åç¼å·" width="155"></el-table-column> |
| | | <el-table-column prop="contractorName" label="æ¿ä¿®åä½" width="150" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="å®å·¥ç¶æ" width="100"> |
| | | <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="engineeringNo" label="å·¥ç¨ç¼å·" width="145"></el-table-column> |
| | | <el-table-column label="å¼å·¥ / å®å·¥" width="190"> |
| | | <template slot-scope="{ row }">{{ formatPeriod(row.startDate, row.finishDate) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="contractorPerson" label="æ¿ä¿®äºº" width="100"></el-table-column> |
| | | <el-table-column label="å®å·¥å¤çæ
åµ" min-width="260"> |
| | | <template slot-scope="{ row }"> |
| | | <div v-if="row.solutions && row.solutions.length" class="detail-list"> |
| | | <div v-for="(item, index) in row.solutions" :key="index" class="detail-line"> |
| | | <span class="detail-index">{{ index + 1 }}</span> |
| | | <span>{{ item.content || '-' }}</span> |
| | | </div> |
| | | </div> |
| | | <span v-else class="muted">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ´æ¢å¨æ" min-width="300"> |
| | | <template slot-scope="{ row }"> |
| | | <div v-if="row.materials && row.materials.length" class="detail-list"> |
| | | <div v-for="(item, index) in row.materials" :key="index" class="material-line"> |
| | | <span class="detail-index">{{ index + 1 }}</span> |
| | | <div> |
| | | <div><strong>{{ item.name || '-' }}</strong><span v-if="item.specification"> / {{ item.specification }}</span><span v-if="item.quantity != null"> Ã {{ item.quantity }}{{ item.unit || '' }}</span></div> |
| | | <div class="material-meta">{{ materialMeta(item) }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <span v-else class="muted">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="qualityInfo" label="ä¿®åè´¨éæ
åµ" min-width="180" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="reasonAnalysis" label="åå åæ" min-width="180" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="submitTime" label="æäº¤æ¶é´" width="160"></el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import api from '@/api' |
| | | import { completionStatuses, statusMeta } from '@/utils/dict' |
| | | |
| | | export default { |
| | | name: 'CompletionQuery', |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | completionStatuses, |
| | | rows: [], |
| | | filters: { |
| | | keyword: '', |
| | | contractorName: '', |
| | | completionStatus: '', |
| | | completionDate: [] |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | displayRows() { |
| | | const rows = [] |
| | | this.rows.forEach((fault, faultIndex) => { |
| | | const workOrders = fault.workOrders || [] |
| | | const total = workOrders.length || 1 |
| | | if (!workOrders.length) { |
| | | rows.push({ ...fault, faultIndex: faultIndex + 1, faultRowspan: 1, rowKey: fault.faultId + '-empty' }) |
| | | return |
| | | } |
| | | workOrders.forEach((workOrder, index) => { |
| | | rows.push({ |
| | | ...fault, |
| | | ...workOrder, |
| | | faultIndex: faultIndex + 1, |
| | | faultRowspan: index === 0 ? total : 0, |
| | | rowKey: fault.faultId + '-' + workOrder.workOrderId |
| | | }) |
| | | }) |
| | | }) |
| | | return rows |
| | | } |
| | | }, |
| | | created() { |
| | | this.load() |
| | | }, |
| | | methods: { |
| | | load() { |
| | | this.loading = true |
| | | const range = this.filters.completionDate || [] |
| | | api.get('/work-orders/completion/query', { |
| | | params: { |
| | | keyword: this.filters.keyword || undefined, |
| | | contractorName: this.filters.contractorName || undefined, |
| | | completionStatus: this.filters.completionStatus || undefined, |
| | | startDate: range[0] || undefined, |
| | | endDate: range[1] || undefined |
| | | } |
| | | }).then(res => { |
| | | this.rows = res.data || [] |
| | | }).finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | reset() { |
| | | this.filters = { keyword: '', contractorName: '', completionStatus: '', completionDate: [] } |
| | | this.load() |
| | | }, |
| | | completionMeta(value) { |
| | | return statusMeta(completionStatuses, value) |
| | | }, |
| | | spanMethod({ row, columnIndex }) { |
| | | if (columnIndex <= 6) { |
| | | return row.faultRowspan === 0 ? { rowspan: 0, colspan: 0 } : { rowspan: row.faultRowspan, colspan: 1 } |
| | | } |
| | | return { rowspan: 1, colspan: 1 } |
| | | }, |
| | | formatPeriod(startDate, finishDate) { |
| | | return [startDate || '-', finishDate || '-'].join(' è³ ') |
| | | }, |
| | | materialMeta(item) { |
| | | const parts = [] |
| | | if (item.technicalParameter) parts.push(item.technicalParameter) |
| | | if (item.source) parts.push('æ¥æºï¼' + item.source) |
| | | if (item.outboundNo) parts.push('åºåºåï¼' + item.outboundNo) |
| | | if (item.certificate) parts.push('åæ ¼è¯æï¼' + item.certificate) |
| | | if (item.remark) parts.push('夿³¨ï¼' + item.remark) |
| | | return parts.join('ï¼') || '-' |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .table-panel { |
| | | overflow-x: auto; |
| | | } |
| | | .detail-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 5px; |
| | | } |
| | | .detail-line, |
| | | .material-line { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | gap: 6px; |
| | | line-height: 20px; |
| | | } |
| | | .material-line > div { |
| | | min-width: 0; |
| | | } |
| | | .detail-index { |
| | | flex: 0 0 auto; |
| | | min-width: 16px; |
| | | height: 16px; |
| | | margin-top: 2px; |
| | | border-radius: 50%; |
| | | color: #fff; |
| | | background: #7b8fa6; |
| | | font-size: 11px; |
| | | line-height: 16px; |
| | | text-align: center; |
| | | } |
| | | .material-meta { |
| | | color: #7b8a9a; |
| | | font-size: 12px; |
| | | } |
| | | .muted { |
| | | color: #a3afbc; |
| | | } |
| | | </style> |
| | |
| | | |
| | | <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-cascader v-model="form.pid" :options="parentOptions" :props="parentProps" :show-all-levels="false" filterable clearable placeholder="è¯·éæ©ä¸çº§èå" style="width:100%"></el-cascader> |
| | | </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> |
| | |
| | | dialogVisible: false, |
| | | saving: false, |
| | | form: {}, |
| | | parent: null, |
| | | rules: { name: [{ required: true, message: '请è¾å
¥èååç§°', trigger: 'blur' }] }, |
| | | buttonDialogVisible: false, |
| | | buttonSaving: false, |
| | |
| | | } |
| | | }, |
| | | computed: { |
| | | parentName() { |
| | | if (!this.parent) return this.form.pid && this.form.pid !== 0 ? 'ä¸çº§èå' : 'æ ï¼ä¸çº§èåï¼' |
| | | return this.parent.name |
| | | parentProps() { |
| | | return { value: 'id', label: 'name', children: 'children', checkStrictly: true, emitPath: false, expandTrigger: 'hover' } |
| | | }, |
| | | selectedParent() { |
| | | const pid = String(this.form.pid || '0') |
| | | if (pid === '0') return null |
| | | return this.flat.find(item => String(item.id) === pid) || null |
| | | }, |
| | | parentOptions() { |
| | | const excluded = this.excludedMenuIds() |
| | | const allowedType = this.form.menuType === 2 ? 1 : 0 |
| | | const nodes = this.buildParentOptions(this.tree, allowedType, excluded) |
| | | return this.form.menuType === 2 ? nodes : [{ id: '0', name: 'æ ï¼ä¸çº§èåï¼', children: nodes }] |
| | | }, |
| | | typeOptions() { |
| | | if (this.parent && this.parent.menuType === 1) { |
| | | if (this.selectedParent && this.selectedParent.menuType === 1) { |
| | | return [{ label: 'æé®', value: 2 }] |
| | | } |
| | | if (this.parent && this.parent.menuType === 0) { |
| | | if (this.selectedParent && this.selectedParent.menuType === 0) { |
| | | return [{ label: 'ç®å½', value: 0 }, { label: 'èå', value: 1 }] |
| | | } |
| | | if (this.form.id && this.form.menuType === 2) { |
| | |
| | | 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, |
| | | pid: parent ? String(parent.id) : '0', |
| | | name: '', |
| | | url: '', |
| | | icon: menuType === 2 ? '' : 'el-icon-tickets', |
| | |
| | | this.dialogVisible = true |
| | | }, |
| | | openEdit(row) { |
| | | this.parent = this.flat.find(item => item.id === row.pid) || null |
| | | this.form = { ...row } |
| | | this.form = { ...row, pid: row.pid == null || row.pid === 0 ? '0' : String(row.pid) } |
| | | this.dialogVisible = true |
| | | }, |
| | | save() { |
| | |
| | | return |
| | | } |
| | | const payload = { ...this.form } |
| | | if (payload.pid === null || payload.pid === undefined || payload.pid === '') payload.pid = '0' |
| | | if (payload.menuType === 2) { |
| | | payload.url = null |
| | | payload.icon = null |
| | |
| | | }).finally(() => { this.saving = false }) |
| | | }) |
| | | }, |
| | | excludedMenuIds() { |
| | | const excluded = new Set() |
| | | const walk = (nodes, underSelf) => { |
| | | ;(nodes || []).forEach(node => { |
| | | const next = underSelf || String(node.id) === String(this.form.id) |
| | | if (next) excluded.add(String(node.id)) |
| | | walk(node.children, next) |
| | | }) |
| | | } |
| | | walk(this.tree, false) |
| | | return excluded |
| | | }, |
| | | buildParentOptions(nodes, allowedType, excluded) { |
| | | return (nodes || []) |
| | | .filter(node => node.menuType === allowedType && !excluded.has(String(node.id))) |
| | | .map(node => ({ |
| | | id: String(node.id), |
| | | name: node.name, |
| | | children: this.buildParentOptions(node.children, allowedType, excluded) |
| | | })) |
| | | }, |
| | | remove(row) { |
| | | this.$confirm(`ç¡®å®å é¤â${row.name}âåï¼`, 'å é¤ç¡®è®¤', { type: 'warning' }) |
| | | .then(() => api.delete('/system/menu/' + row.id)) |
| | |
| | | - é误æ¥å¿ç®åè¦çè¿å
¥ Spring MVC å
¨å±å¼å¸¸å¤ççå¼å¸¸ï¼å¯å¨é¶æ®µå¼å¸¸ã弿¥çº¿ç¨å¼å¸¸è¥ç»è¿è¯¥å¤çå¨ï¼æä¸ä¼èªå¨è®°å½ã |
| | | - æ¬è½®ä»£ç ä¸äº¤æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | - `.codex-run`ãOffice éæä»¶å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` ä»ä¸ºæ¬å°ä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | |
| | | ## ååã2026-10-06 èè°æ¶å°¾ï¼éæ± 23-25ï¼ |
| | | |
| | | | åºå· | éæ± | ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | --- | |
| | | | 23 | ç³»ç»åç§°ãçæ¬æ¹ä¸ºå
³é®è¯é
ç½®ï¼çæ¬çº¢è²æ¾ç¤º | 已宿 | codename 为 `rk_SystemName`ã`rk_SystemVer`ï¼ç»å½é¡µã页头ãLogo åæµè§å¨æ é¢ç»ä¸è¯»åé
ç½®ï¼é¡µå¤´çæ¬ä¸ºçº¢è² | |
| | | | 24 | å®å·¥ä¿¡æ¯æ¥é
ï¼å¤æ
éå夿¡å®å·¥æç»å±ç¤º | 已宿 | å¢å `/completion/query` å `GET /api/work-orders/completion/query`ï¼ææ
éæ±æ»ï¼å䏿
éçå¤å¼ 派工åå±å¼ä¸ºå¤è¡ï¼æ
éä¿¡æ¯åä½¿ç¨ `rowspan` 纵ååå¹¶ï¼å¤çæ
åµåå¨ææ¯æå¤æ¡ | |
| | | | 25 | èå管çç¼è¾æ¶å¯ä¿®æ¹ç¶èç¹ | 已宿 | ä¸çº§èåæ¹ä¸ºçº§èéæ©å¨ï¼æé¤èªèº«åå
¨é¨ä¸çº§ï¼å端å¢å ç¶èç¹ãèªå¼ç¨å循ç¯å±çº§æ ¡éª | |
| | | |
| | | ### 14.1 主è¦åæ´æä»¶ |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/CompletionFaultQueryItem.java`ï¼æ°å¢ï¼ |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/controller/WorkOrderController.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-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java` |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/completion/CompletionQuery.vue`ï¼æ°å¢ï¼ |
| | | - `emergency-repair-web/src/views/system/MenuManage.vue` |
| | | - `emergency-repair-web/src/components/AppHeader.vue`ã`Login.vue`ã`RepairLogo.vue`ã`layout/AppLayout.vue`ã`router/index.js`ã`utils/sensitive.js`ã`main.js` |
| | | - `emergency-repair-web/public/sensitive-words.json`ã`public/index.html` |
| | | - `requirement/ææè¯åå
¸.json` |
| | | |
| | | ### 14.2 å®å·¥ä¿¡æ¯æ¥é
å£å¾ |
| | | |
| | | - ç»æææ
éæ±æ»ï¼ä¸ææ´¾å·¥åæ±æ»ã |
| | | - ä¸ä¸ªæ
éæå¤å¼ 已填æ¥å®å·¥ä¿¡æ¯çæ´¾å·¥åæ¶ï¼è¡¨æ ¼å±å¼ä¸ºå¤è¡ï¼åºå·ãæ
éç¼å·ãrk_Xå·ã设å¤åç§°ãå·¥ç¨åç§°åæåæ
åµã䏿¥åä½ã䏿¥æ¥æè¿ 7 åå
±ç¨æ
éè¡ï¼ä½¿ç¨åå
æ ¼çºµååå¹¶ã |
| | | - 派工åç¼å·ãæ¿ä¿®åä½ãå®å·¥ç¶æãå·¥ç¨ç¼å·ãå¼å·¥/å®å·¥ãæ¿ä¿®äººãå®å·¥å¤çæ
åµãæ´æ¢å¨æãä¿®åè´¨éãåå åæãæäº¤æ¶é´ææ´¾å·¥åå±ç¤ºã |
| | | - æ¥è¯¢æ¯æå
³é®è¯ãæ¿ä¿®åä½ãå®å·¥ç¶æåå®å·¥æ¥æï¼å
³é®è¯åæ¶å¹é
æ
éãæ´¾å·¥åãå·¥ç¨ç¼å·ãæ¿ä¿®äººãå¤çæ
åµå卿ã |
| | | |
| | | ### 14.3 éªè¯ç»æ |
| | | |
| | | - å端 `mvn -B -DskipTests compile` éè¿ï¼å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req23-25.js` éè¿ï¼ç³»ç»åç§°ãçæ¬åçº¢è²æ ·å¼æ£å¸¸ï¼å®å·¥æ¥é
æ£å¸¸ï¼èåç¼è¾ç¶èç¹âæ
é管çâå¯ç¼è¾ã |
| | | - ä¸´æ¶æé å䏿
éä¸¤å¼ æ´¾å·¥åã两æ¡å®å·¥è®°å½ï¼æ¥å£è¿å 1 个æ
éèç¹å 2 å¼ æ´¾å·¥åï¼å带å¤çæ
åµå卿æç»ã |
| | | - æµè§å¨æ¾ç¤º 2 å¼ æ´¾å·¥åè¡ï¼æ
éç¼å·åªåºç° 1 次ï¼7 个æ
éä¿¡æ¯åå为 `rowspan=2`ï¼ä¸¤å¼ åçæç»åæ£å¸¸ï¼æªå¾ `.codex-run/completion-query-merge-verified.png`ãä¸´æ¶æ°æ®å·²æ¸
çã |
| | | - èå级èç¶èç¹æ¥å£å®æµå¯æ£å¸¸ç§»å¨åèåï¼éæ©èªèº«æä¸çº§æ¶è¢«å端æç»ï¼ä¸´æ¶èåå·²æ¸
çã |
| | | |
| | | ### 14.4 å½åæå¡ä¸æ¥ææ³¨æ |
| | | |
| | | - å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge è°è¯ç«¯å£ `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | - å®å·¥æ¥é
å½ååªå±ç¤ºå·²æ `T_COMPLETION_INFO` å®å·¥å¡«æ¥è®°å½ç派工åï¼å¹¶ä¸æ¥å£æªå页ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | - `.codex-run`ãOffice éæä»¶å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` ä»ä¸ºæ¬å°ä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | |
| | | 17ãè§è²ç®¡çç¨æ·è®¾ç½®å¢å ä¸ä¸ªå
¨é¨/å·²è®¾ç½®åæ¢åè½ |
| | | 18ãå¢å åè½æ¾ç¤ºç»ç»æºæï¼é¨é¨æ è¡¨ï¼ |
| | | 19ãæ¿ä¿®åä½åºè¯¥å°é¨é¨è¡¨éæ¾DEPT_TYPEå为cjçè®°å½ |
| | | |
| | | 20ãèåæé管çï¼åæé管çåºè¯¥å2æ¥ï¼ 第1æ¥è®¾ç½®ç§æ·æéï¼ç¬¬2æ¥è®¾ç½®è§è²æéï¼ ç®åç¨æ·æ¬èº«æ¯æ²¡æç§æ·çï¼æä»¥è¿ä¸ªç§æ·æé设置就没å¿
è¦äºï¼ä¿çè§è²æé设置就å¯ä»¥äºï¼æéæ£æ¥æ¡è¦åæ¾ |
| | | 21ãèåæé管çï¼è¿ä¸ªèåä¸è¦ï¼å¨è§è²ç®¡çå¢å ä¸ä¸ªæé设置æé®ï¼ç±»ä¼¼ç¨æ·è®¾ç½®é£ä¸ªæé®é£æ · |
| | | 22ãç³»ç»ç®¡çä¸é¢å¢å 2级èåç»èç¹ï¼ ç³»ç»é
ç½®ï¼é¤äºæéé
ç½®ãæ¥å¿ç®¡ç管çä¸é¢åªäºèåå¤ï¼å
¶ä»èåæ¾å°ç³»ç»é
ç½®ä¸é¢ï¼ãæéé
ç½®ï¼ç¨æ·ç®¡çãè§è²ç®¡çãèå管çï¼ãæ¥å¿ç®¡çï¼ç»å½æ¥å¿ãæä½æ¥å¿ãé误æ¥å¿ï¼ |
| | | é误æ¥å¿ä¸ºæ°å¢åè½ï¼è®°å½ç¨åºè¿è¡åºç°çbug |
| | | |
| | | 23ãç³»ç»åç§°ãç½ç»ççåæå
³é®è¯é
ç½®ï¼codenameåå«ä¸ºrk_SystemNameãrk_SystemVer çæ¬ç¨çº¢è²å使¾ç¤º |
| | | 24ãå¢å å®å·¥ä¿¡æ¯æ¥é
åè½ï¼å¨ä¸ä¸ªè¡¨å±ç¤ºå¤ä¸ªæ
éåå®å·¥ä¿¡æ¯å±ç¤ºï¼å
¶ä¸å®å·¥å¤çæ
åµãæ´æ¢å¨æé½æ¯å¤ä¸ªï¼ |
| | | 25ãèå管çç¼è¾æ¶ï¼åºè¯¥å¯ä»¥ä¿®æ¹ç¶èç¹ |
| | | |
| | |
| | | { |
| | | "sensitive_words": [ |
| | | { |
| | | "codename": "rk_SystemName", |
| | | "real_name": "临æ¢ä¿®ç®¡çç³»ç»", |
| | | "scope": "documents,ui,code", |
| | | "note": "ç³»ç»åç§°ï¼ç»å½é¡µã页头念è§å¨æ é¢ç»ä¸ä½¿ç¨è¯¥é
ç½®" |
| | | }, |
| | | { |
| | | "codename": "rk_SystemVer", |
| | | "real_name": "ç½ç»ç", |
| | | "scope": "documents,ui,code", |
| | | "note": "ç³»ç»çæ¬ï¼é¡µå¤´ä¸ä½¿ç¨çº¢è²å使¾ç¤º" |
| | | }, |
| | | { |
| | | "codename": "rk_Xå·", |
| | | "real_name": "è·å·", |
| | | "scope": "documents,ui,code", |