From aa710969e00e9d7e56a276066a239f74d5c49310 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 31 三月 2026 21:47:07 +0800
Subject: [PATCH] #

---
 src/main/webapp/views/basStationErrLog/basStationErrLog.html            |  670 ++++++++++++++
 src/main/java/com/zy/asrs/mapper/BasStationErrLogMapper.java            |    9 
 src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java      |   21 
 src/main/java/com/zy/core/thread/impl/ZyStationV4Thread.java            |    2 
 src/main/java/com/zy/core/thread/support/StationErrLogSupport.java      |  138 +++
 src/main/java/com/zy/asrs/entity/WrkAnalysis.java                       |    8 
 src/main/webapp/static/js/basStationErrLog/basStationErrLog.js          | 1165 +++++++++++++++++++++++++
 src/main/resources/i18n/en-US/legacy.properties                         |    3 
 src/main/java/com/zy/asrs/controller/WrkMastLogController.java          |   16 
 src/main/java/com/zy/asrs/service/impl/BasStationErrLogServiceImpl.java |   11 
 src/main/resources/mapper/BasStationErrLogMapper.xml                    |   29 
 src/main/java/com/zy/asrs/controller/BasStationErrLogController.java    |  129 ++
 src/main/java/com/zy/asrs/service/BasStationErrLogService.java          |    7 
 src/main/webapp/static/js/wrkMastLog/wrkMastLog.js                      |  112 ++
 src/main/java/com/zy/core/thread/impl/ZyStationThread.java              |    2 
 src/main/java/com/zy/core/thread/impl/ZyStationV3Thread.java            |    2 
 src/main/resources/i18n/zh-CN/legacy.properties                         |    3 
 src/main/java/com/zy/core/enums/RedisKeyType.java                       |    1 
 src/main/java/com/zy/asrs/entity/BasStationErrLog.java                  |  211 ++++
 src/main/java/com/zy/core/thread/impl/v5/StationV5StatusReader.java     |    2 
 src/main/resources/sql/20260331_add_station_err_log.sql                 |  172 +++
 21 files changed, 2,702 insertions(+), 11 deletions(-)

diff --git a/src/main/java/com/zy/asrs/controller/BasStationErrLogController.java b/src/main/java/com/zy/asrs/controller/BasStationErrLogController.java
new file mode 100644
index 0000000..4307712
--- /dev/null
+++ b/src/main/java/com/zy/asrs/controller/BasStationErrLogController.java
@@ -0,0 +1,129 @@
+package com.zy.asrs.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.core.annotations.ManagerAuth;
+import com.core.common.BaseRes;
+import com.core.common.Cools;
+import com.core.common.DateUtils;
+import com.core.common.R;
+import com.zy.asrs.entity.BasStationErrLog;
+import com.zy.asrs.service.BasStationErrLogService;
+import com.zy.common.web.BaseController;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+public class BasStationErrLogController extends BaseController {
+
+    @Autowired
+    private BasStationErrLogService basStationErrLogService;
+
+    @RequestMapping(value = "/basStationErrLog/{id}/auth")
+    @ManagerAuth
+    public R get(@PathVariable("id") String id) {
+        return R.ok(basStationErrLogService.getById(String.valueOf(id)));
+    }
+
+    @RequestMapping(value = "/basStationErrLog/list/auth")
+    @ManagerAuth
+    public R list(@RequestParam(defaultValue = "1") Integer curr,
+                  @RequestParam(defaultValue = "10") Integer limit,
+                  @RequestParam(required = false) String orderByField,
+                  @RequestParam(required = false) String orderByType,
+                  @RequestParam(required = false) String condition,
+                  @RequestParam Map<String, Object> param) {
+        QueryWrapper<BasStationErrLog> wrapper = new QueryWrapper<>();
+        excludeTrash(param);
+        convert(param, wrapper);
+        allLike(BasStationErrLog.class, param.keySet(), wrapper, condition);
+        if (!Cools.isEmpty(orderByField)) {
+            wrapper.orderBy(true, "asc".equals(orderByType), humpToLine(orderByField));
+        }
+        return R.ok(basStationErrLogService.page(new Page<>(curr, limit), wrapper));
+    }
+
+    private <T> void convert(Map<String, Object> map, QueryWrapper<T> wrapper) {
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
+            String val = String.valueOf(entry.getValue());
+            String column = humpToLine(entry.getKey());
+            if (val.contains(RANGE_TIME_LINK)) {
+                String[] dates = val.split(RANGE_TIME_LINK);
+                wrapper.ge(column, DateUtils.convert(dates[0]));
+                wrapper.le(column, DateUtils.convert(dates[1]));
+            } else {
+                wrapper.like(column, val);
+            }
+        }
+    }
+
+    @RequestMapping(value = "/basStationErrLog/add/auth")
+    @ManagerAuth
+    public R add(BasStationErrLog basStationErrLog) {
+        basStationErrLogService.save(basStationErrLog);
+        return R.ok();
+    }
+
+    @RequestMapping(value = "/basStationErrLog/update/auth")
+    @ManagerAuth
+    public R update(BasStationErrLog basStationErrLog) {
+        if (Cools.isEmpty(basStationErrLog) || basStationErrLog.getId() == null) {
+            return R.error();
+        }
+        basStationErrLogService.updateById(basStationErrLog);
+        return R.ok();
+    }
+
+    @RequestMapping(value = "/basStationErrLog/delete/auth")
+    @ManagerAuth
+    public R delete(@RequestParam(value = "ids[]") Integer[] ids) {
+        for (Integer id : ids) {
+            basStationErrLogService.removeById(id);
+        }
+        return R.ok();
+    }
+
+    @RequestMapping(value = "/basStationErrLog/export/auth")
+    @ManagerAuth
+    public R export(@RequestBody JSONObject param) {
+        QueryWrapper<BasStationErrLog> wrapper = new QueryWrapper<>();
+        List<String> fields = JSONObject.parseArray(param.getJSONArray("fields").toJSONString(), String.class);
+        Map<String, Object> map = excludeTrash(param.getJSONObject("basStationErrLog"));
+        convert(map, wrapper);
+        List<BasStationErrLog> list = basStationErrLogService.list(wrapper);
+        return R.ok(exportSupport(list, fields));
+    }
+
+    @RequestMapping(value = "/basStationErrLogQuery/auth")
+    @ManagerAuth
+    public R query(String condition) {
+        QueryWrapper<BasStationErrLog> wrapper = new QueryWrapper<>();
+        wrapper.like("id", condition);
+        Page<BasStationErrLog> page = basStationErrLogService.page(new Page<>(0, 10), wrapper);
+        List<Map<String, Object>> result = new ArrayList<>();
+        for (BasStationErrLog basStationErrLog : page.getRecords()) {
+            Map<String, Object> map = new HashMap<>();
+            map.put("id", basStationErrLog.getId());
+            map.put("value", basStationErrLog.getId());
+            result.add(map);
+        }
+        return R.ok(result);
+    }
+
+    @RequestMapping(value = "/basStationErrLog/check/column/auth")
+    @ManagerAuth
+    public R query(@RequestBody JSONObject param) {
+        QueryWrapper<BasStationErrLog> wrapper = new QueryWrapper<BasStationErrLog>()
+                .eq(humpToLine(String.valueOf(param.get("key"))), param.get("val"));
+        if (null != basStationErrLogService.getOne(wrapper)) {
+            return R.parse(BaseRes.REPEAT).add(getComment(BasStationErrLog.class, String.valueOf(param.get("key"))));
+        }
+        return R.ok();
+    }
+}
diff --git a/src/main/java/com/zy/asrs/controller/WrkMastLogController.java b/src/main/java/com/zy/asrs/controller/WrkMastLogController.java
index 5d89021..b01b3d7 100644
--- a/src/main/java/com/zy/asrs/controller/WrkMastLogController.java
+++ b/src/main/java/com/zy/asrs/controller/WrkMastLogController.java
@@ -17,12 +17,22 @@
 import org.springframework.web.bind.annotation.*;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 @RestController
 public class WrkMastLogController extends BaseController {
+
+    private static final Set<String> EXACT_SEARCH_COLUMNS = new HashSet<>(Arrays.asList(
+            "wrk_sts",
+            "io_type",
+            "sta_no",
+            "source_sta_no"
+    ));
 
     @Autowired
     private WrkMastLogService wrkMastLogService;
@@ -65,10 +75,10 @@
                 wrapper.ge(column, DateUtils.convert(dates[0]));
                 wrapper.le(column, DateUtils.convert(dates[1]));
             } else {
-                if ("manu_type".equals(column)) {
-                    wrapper.like(column, val);
-                } else {
+                if (EXACT_SEARCH_COLUMNS.contains(column)) {
                     wrapper.eq(column, val);
+                } else {
+                    wrapper.like(column, val);
                 }
             }
         }
diff --git a/src/main/java/com/zy/asrs/entity/BasStationErrLog.java b/src/main/java/com/zy/asrs/entity/BasStationErrLog.java
new file mode 100644
index 0000000..d315e3f
--- /dev/null
+++ b/src/main/java/com/zy/asrs/entity/BasStationErrLog.java
@@ -0,0 +1,211 @@
+package com.zy.asrs.entity;
+
+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.core.common.Cools;
+import com.core.common.SpringUtils;
+import com.zy.asrs.service.BasStationService;
+import com.zy.asrs.service.BasWrkIotypeService;
+import com.zy.asrs.service.BasWrkStatusService;
+import com.zy.system.entity.User;
+import com.zy.system.service.UserService;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+@Data
+@TableName("asr_bas_station_err_log")
+public class BasStationErrLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "缂栧彿")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "宸ヤ綔鍙�")
+    @TableField("wrk_no")
+    private Integer wrkNo;
+
+    @ApiModelProperty(value = "鍙戠敓鏃堕棿")
+    @TableField("start_time")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date startTime;
+
+    @ApiModelProperty(value = "缁撴潫鏃堕棿")
+    @TableField("end_time")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date endTime;
+
+    @ApiModelProperty(value = "宸ヤ綔鐘舵��")
+    @TableField("wrk_sts")
+    private Long wrkSts;
+
+    @ApiModelProperty(value = "鍏ュ嚭搴撶被鍨�")
+    @TableField("io_type")
+    private Integer ioType;
+
+    @ApiModelProperty(value = "绔欑偣鍙�")
+    @TableField("station_id")
+    private Integer stationId;
+
+    @ApiModelProperty(value = "鐩爣搴撲綅")
+    @TableField("loc_no")
+    private String locNo;
+
+    @ApiModelProperty(value = "鐩爣绔�")
+    @TableField("sta_no")
+    private Integer staNo;
+
+    @ApiModelProperty(value = "婧愮珯")
+    @TableField("source_sta_no")
+    private Integer sourceStaNo;
+
+    @ApiModelProperty(value = "婧愬簱浣�")
+    @TableField("source_loc_no")
+    private String sourceLocNo;
+
+    @ApiModelProperty(value = "鏉$爜")
+    private String barcode;
+
+    @ApiModelProperty(value = "寮傚父鐮�")
+    @TableField("err_code")
+    private Integer errCode;
+
+    @ApiModelProperty(value = "寮傚父")
+    private String error;
+
+    @ApiModelProperty(value = "寮傚父鎯呭喌 1: 鏈鐞�  2: 宸蹭慨澶�")
+    private Integer status;
+
+    @ApiModelProperty(value = "娣诲姞鏃堕棿")
+    @TableField("create_time")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    @ApiModelProperty(value = "娣诲姞浜哄憳")
+    @TableField("create_by")
+    private Long createBy;
+
+    @ApiModelProperty(value = "淇敼鏃堕棿")
+    @TableField("update_time")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
+    @ApiModelProperty(value = "淇敼浜哄憳")
+    @TableField("update_by")
+    private Long updateBy;
+
+    @ApiModelProperty(value = "澶囨敞")
+    private String memo;
+
+    @ApiModelProperty(value = "绯荤粺鐘舵�佹暟鎹�")
+    @TableField("system_status")
+    private String systemStatus;
+
+    public String getStartTime$() {
+        return formatDate(startTime);
+    }
+
+    public String getEndTime$() {
+        return formatDate(endTime);
+    }
+
+    public String getWrkSts$() {
+        if (wrkSts == null) {
+            return null;
+        }
+        BasWrkStatusService service = SpringUtils.getBean(BasWrkStatusService.class);
+        if (service == null) {
+            return String.valueOf(wrkSts);
+        }
+        BasWrkStatus basWrkStatus = service.getById(wrkSts);
+        return basWrkStatus == null ? String.valueOf(wrkSts) : basWrkStatus.getWrkDesc();
+    }
+
+    public String getIoType$() {
+        if (ioType == null) {
+            return null;
+        }
+        BasWrkIotypeService service = SpringUtils.getBean(BasWrkIotypeService.class);
+        if (service == null) {
+            return String.valueOf(ioType);
+        }
+        BasWrkIotype basWrkIotype = service.getById(ioType);
+        return basWrkIotype == null ? String.valueOf(ioType) : basWrkIotype.getIoDesc();
+    }
+
+    public String getStationLabel$() {
+        if (stationId == null) {
+            return null;
+        }
+        BasStationService service = SpringUtils.getBean(BasStationService.class);
+        if (service == null) {
+            return String.valueOf(stationId);
+        }
+        BasStation basStation = service.getById(stationId);
+        if (basStation == null || Cools.isEmpty(basStation.getStationAlias())) {
+            return String.valueOf(stationId);
+        }
+        return stationId + " - " + basStation.getStationAlias();
+    }
+
+    public String getStatus$() {
+        if (status == null) {
+            return null;
+        }
+        switch (status) {
+            case 1:
+                return "鏈鐞�";
+            case 2:
+                return "宸蹭慨澶�";
+            default:
+                return String.valueOf(status);
+        }
+    }
+
+    public String getCreateTime$() {
+        return formatDate(createTime);
+    }
+
+    public String getCreateBy$() {
+        if (createBy == null) {
+            return null;
+        }
+        UserService service = SpringUtils.getBean(UserService.class);
+        if (service == null) {
+            return String.valueOf(createBy);
+        }
+        User user = service.getById(createBy);
+        return user == null ? String.valueOf(createBy) : String.valueOf(user.getId());
+    }
+
+    public String getUpdateTime$() {
+        return formatDate(updateTime);
+    }
+
+    public String getUpdateBy$() {
+        if (updateBy == null) {
+            return null;
+        }
+        UserService service = SpringUtils.getBean(UserService.class);
+        if (service == null) {
+            return String.valueOf(updateBy);
+        }
+        User user = service.getById(updateBy);
+        return user == null ? String.valueOf(updateBy) : String.valueOf(user.getId());
+    }
+
+    private String formatDate(Date value) {
+        if (Cools.isEmpty(value)) {
+            return "";
+        }
+        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value);
+    }
+}
diff --git a/src/main/java/com/zy/asrs/entity/WrkAnalysis.java b/src/main/java/com/zy/asrs/entity/WrkAnalysis.java
index e17b96b..df43106 100644
--- a/src/main/java/com/zy/asrs/entity/WrkAnalysis.java
+++ b/src/main/java/com/zy/asrs/entity/WrkAnalysis.java
@@ -139,6 +139,14 @@
     @TableField("rgv_fault_duration_ms")
     private Long rgvFaultDurationMs;
 
+    @ApiModelProperty(value = "杈撻�佺珯鐐规晠闅滄鏁�")
+    @TableField("station_fault_count")
+    private Integer stationFaultCount;
+
+    @ApiModelProperty(value = "杈撻�佺珯鐐规晠闅滆�楁椂姣")
+    @TableField("station_fault_duration_ms")
+    private Long stationFaultDurationMs;
+
     @ApiModelProperty(value = "鏁版嵁瀹屾暣鎬�")
     @TableField("metric_completeness")
     private String metricCompleteness;
diff --git a/src/main/java/com/zy/asrs/mapper/BasStationErrLogMapper.java b/src/main/java/com/zy/asrs/mapper/BasStationErrLogMapper.java
new file mode 100644
index 0000000..7a09f39
--- /dev/null
+++ b/src/main/java/com/zy/asrs/mapper/BasStationErrLogMapper.java
@@ -0,0 +1,9 @@
+package com.zy.asrs.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zy.asrs.entity.BasStationErrLog;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface BasStationErrLogMapper extends BaseMapper<BasStationErrLog> {
+}
diff --git a/src/main/java/com/zy/asrs/service/BasStationErrLogService.java b/src/main/java/com/zy/asrs/service/BasStationErrLogService.java
new file mode 100644
index 0000000..7c68e39
--- /dev/null
+++ b/src/main/java/com/zy/asrs/service/BasStationErrLogService.java
@@ -0,0 +1,7 @@
+package com.zy.asrs.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zy.asrs.entity.BasStationErrLog;
+
+public interface BasStationErrLogService extends IService<BasStationErrLog> {
+}
diff --git a/src/main/java/com/zy/asrs/service/impl/BasStationErrLogServiceImpl.java b/src/main/java/com/zy/asrs/service/impl/BasStationErrLogServiceImpl.java
new file mode 100644
index 0000000..9610b18
--- /dev/null
+++ b/src/main/java/com/zy/asrs/service/impl/BasStationErrLogServiceImpl.java
@@ -0,0 +1,11 @@
+package com.zy.asrs.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zy.asrs.entity.BasStationErrLog;
+import com.zy.asrs.mapper.BasStationErrLogMapper;
+import com.zy.asrs.service.BasStationErrLogService;
+import org.springframework.stereotype.Service;
+
+@Service("basStationErrLogService")
+public class BasStationErrLogServiceImpl extends ServiceImpl<BasStationErrLogMapper, BasStationErrLog> implements BasStationErrLogService {
+}
diff --git a/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java b/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
index 6bbaac2..da83645 100644
--- a/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
@@ -46,6 +46,7 @@
     private final BasCrnpErrLogService basCrnpErrLogService;
     private final BasDualCrnpErrLogService basDualCrnpErrLogService;
     private final BasRgvErrLogService basRgvErrLogService;
+    private final BasStationErrLogService basStationErrLogService;
     private final BasStationService basStationService;
     private final BasWrkStatusService basWrkStatusService;
     private final WrkMastService wrkMastService;
@@ -54,6 +55,7 @@
                                   BasCrnpErrLogService basCrnpErrLogService,
                                   BasDualCrnpErrLogService basDualCrnpErrLogService,
                                   BasRgvErrLogService basRgvErrLogService,
+                                  BasStationErrLogService basStationErrLogService,
                                   BasStationService basStationService,
                                   BasWrkStatusService basWrkStatusService,
                                   WrkMastService wrkMastService) {
@@ -61,6 +63,7 @@
         this.basCrnpErrLogService = basCrnpErrLogService;
         this.basDualCrnpErrLogService = basDualCrnpErrLogService;
         this.basRgvErrLogService = basRgvErrLogService;
+        this.basStationErrLogService = basStationErrLogService;
         this.basStationService = basStationService;
         this.basWrkStatusService = basWrkStatusService;
         this.wrkMastService = wrkMastService;
@@ -89,6 +92,8 @@
         entity.setDualCrnFaultDurationMs(defaultLong(entity.getDualCrnFaultDurationMs()));
         entity.setRgvFaultCount(defaultInt(entity.getRgvFaultCount()));
         entity.setRgvFaultDurationMs(defaultLong(entity.getRgvFaultDurationMs()));
+        entity.setStationFaultCount(defaultInt(entity.getStationFaultCount()));
+        entity.setStationFaultDurationMs(defaultLong(entity.getStationFaultDurationMs()));
         entity.setMetricCompleteness(METRIC_PARTIAL);
         entity.setUpdateTime(now);
         this.saveOrUpdate(entity);
@@ -243,6 +248,8 @@
         entity.setDualCrnFaultDurationMs(faultSummary.dualDurationMs);
         entity.setRgvFaultCount(faultSummary.rgvCount);
         entity.setRgvFaultDurationMs(faultSummary.rgvDurationMs);
+        entity.setStationFaultCount(faultSummary.stationCount);
+        entity.setStationFaultDurationMs(faultSummary.stationDurationMs);
         entity.setMetricCompleteness(resolveMetricCompleteness(wrkMast, entity));
         entity.setUpdateTime(time);
         this.saveOrUpdate(entity);
@@ -610,6 +617,7 @@
             addDeviceFaultDuration(durationMap, "鍗曞爢鍨涙満", item.getCrnNo(), item.getCrnFaultDurationMs());
             addDeviceFaultDuration(durationMap, "鍙屽伐浣嶅爢鍨涙満", item.getDualCrnNo(), item.getDualCrnFaultDurationMs());
             addDeviceFaultDuration(durationMap, "RGV", item.getRgvNo(), item.getRgvFaultDurationMs());
+            addDeviceFaultDuration(durationMap, "杈撻�佺珯鐐�", null, item.getStationFaultDurationMs());
         }
         List<Map<String, Object>> result = new ArrayList<>();
         durationMap.forEach((name, durationMs) -> result.add(slice(name, durationMs)));
@@ -680,6 +688,7 @@
         target.put("crnFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getCrnFaultDurationMs()));
         target.put("dualCrnFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getDualCrnFaultDurationMs()));
         target.put("rgvFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getRgvFaultDurationMs()));
+        target.put("stationFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getStationFaultDurationMs()));
         target.put("metricCompleteness", analysis == null ? METRIC_PARTIAL : analysis.getMetricCompleteness());
     }
 
@@ -691,14 +700,17 @@
         List<BasCrnpErrLog> crnList = basCrnpErrLogService.list(new QueryWrapper<BasCrnpErrLog>().eq("wrk_no", wrkNo));
         List<BasDualCrnpErrLog> dualList = basDualCrnpErrLogService.list(new QueryWrapper<BasDualCrnpErrLog>().eq("wrk_no", wrkNo));
         List<BasRgvErrLog> rgvList = basRgvErrLogService.list(new QueryWrapper<BasRgvErrLog>().eq("task_no", wrkNo));
+        List<BasStationErrLog> stationList = basStationErrLogService.list(new QueryWrapper<BasStationErrLog>().eq("wrk_no", wrkNo));
         summary.crnCount = crnList.size();
         summary.crnDurationMs = durationMs(crnList, finishTime);
         summary.dualCount = dualList.size();
         summary.dualDurationMs = durationMs(dualList, finishTime);
         summary.rgvCount = rgvList.size();
         summary.rgvDurationMs = durationMs(rgvList, finishTime);
-        summary.totalCount = summary.crnCount + summary.dualCount + summary.rgvCount;
-        summary.totalDurationMs = summary.crnDurationMs + summary.dualDurationMs + summary.rgvDurationMs;
+        summary.stationCount = stationList.size();
+        summary.stationDurationMs = durationMs(stationList, finishTime);
+        summary.totalCount = summary.crnCount + summary.dualCount + summary.rgvCount + summary.stationCount;
+        summary.totalDurationMs = summary.crnDurationMs + summary.dualDurationMs + summary.rgvDurationMs + summary.stationDurationMs;
         summary.hasFault = summary.totalCount > 0 ? 1 : 0;
         return summary;
     }
@@ -717,6 +729,9 @@
             } else if (item instanceof BasRgvErrLog) {
                 startTime = ((BasRgvErrLog) item).getStartTime();
                 endTime = ((BasRgvErrLog) item).getEndTime();
+            } else if (item instanceof BasStationErrLog) {
+                startTime = ((BasStationErrLog) item).getStartTime();
+                endTime = ((BasStationErrLog) item).getEndTime();
             }
             if (startTime == null) {
                 continue;
@@ -977,6 +992,8 @@
         private long dualDurationMs;
         private int rgvCount;
         private long rgvDurationMs;
+        private int stationCount;
+        private long stationDurationMs;
     }
 
     private static class BucketAccumulator {
diff --git a/src/main/java/com/zy/core/enums/RedisKeyType.java b/src/main/java/com/zy/core/enums/RedisKeyType.java
index fef34ca..cd1b8e3 100644
--- a/src/main/java/com/zy/core/enums/RedisKeyType.java
+++ b/src/main/java/com/zy/core/enums/RedisKeyType.java
@@ -24,6 +24,7 @@
     DEVICE_ERR_ACTIVE_RGV("device_err_active_rgv_"),
     DEVICE_ERR_ACTIVE_CRN("device_err_active_crn_"),
     DEVICE_ERR_ACTIVE_DUAL_CRN("device_err_active_dual_crn_"),
+    DEVICE_ERR_ACTIVE_STATION("device_err_active_station_"),
     DEVICE_STATION_MOVE_RESET("device_station_move_reset_"),
 
     CRN_SEND_COMMAND_LOCK("crn_send_command_lock_"),
diff --git a/src/main/java/com/zy/core/thread/impl/ZyStationThread.java b/src/main/java/com/zy/core/thread/impl/ZyStationThread.java
index a4a1e98..769a168 100644
--- a/src/main/java/com/zy/core/thread/impl/ZyStationThread.java
+++ b/src/main/java/com/zy/core/thread/impl/ZyStationThread.java
@@ -9,6 +9,7 @@
 import com.zy.core.network.DeviceConnectPool;
 import com.zy.core.thread.StationThread;
 import com.zy.core.thread.support.RecentStationArrivalTracker;
+import com.zy.core.thread.support.StationErrLogSupport;
 import com.alibaba.fastjson.JSON;
 import com.core.common.DateUtils;
 import com.core.common.SpringUtils;
@@ -160,6 +161,7 @@
         }
 
         OutputQueue.DEVP.offer(MessageFormat.format("銆恵0}銆慬id:{1}] <<<<< 瀹炴椂鏁版嵁鏇存柊鎴愬姛",DateUtils.convert(new Date()), deviceConfig.getDeviceNo()));
+        StationErrLogSupport.sync(deviceConfig, redisUtil, statusList);
 
         if (System.currentTimeMillis() - deviceDataLogTime > deviceLogCollectTime) {
             //淇濆瓨鏁版嵁璁板綍
diff --git a/src/main/java/com/zy/core/thread/impl/ZyStationV3Thread.java b/src/main/java/com/zy/core/thread/impl/ZyStationV3Thread.java
index 6accb28..5abc04a 100644
--- a/src/main/java/com/zy/core/thread/impl/ZyStationV3Thread.java
+++ b/src/main/java/com/zy/core/thread/impl/ZyStationV3Thread.java
@@ -31,6 +31,7 @@
 import com.zy.core.network.ZyStationConnectDriver;
 import com.zy.core.network.entity.ZyStationStatusEntity;
 import com.zy.core.thread.support.RecentStationArrivalTracker;
+import com.zy.core.thread.support.StationErrLogSupport;
 import com.zy.core.utils.DeviceLogRedisKeyBuilder;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
@@ -165,6 +166,7 @@
         }
 
         OutputQueue.DEVP.offer(MessageFormat.format("銆恵0}銆慬id:{1}] <<<<< 瀹炴椂鏁版嵁鏇存柊鎴愬姛", DateUtils.convert(new Date()), deviceConfig.getDeviceNo()));
+        StationErrLogSupport.sync(deviceConfig, redisUtil, statusList);
 
         if (System.currentTimeMillis() - deviceDataLogTime > deviceLogCollectTime) {
             DeviceDataLog deviceDataLog = new DeviceDataLog();
diff --git a/src/main/java/com/zy/core/thread/impl/ZyStationV4Thread.java b/src/main/java/com/zy/core/thread/impl/ZyStationV4Thread.java
index 46cb90d..a08a715 100644
--- a/src/main/java/com/zy/core/thread/impl/ZyStationV4Thread.java
+++ b/src/main/java/com/zy/core/thread/impl/ZyStationV4Thread.java
@@ -32,6 +32,7 @@
 import com.zy.core.network.entity.ZyStationStatusEntity;
 import com.zy.core.thread.impl.v5.StationMoveSegmentExecutor;
 import com.zy.core.thread.support.RecentStationArrivalTracker;
+import com.zy.core.thread.support.StationErrLogSupport;
 import com.zy.core.utils.DeviceLogRedisKeyBuilder;
 import com.zy.system.entity.Config;
 import com.zy.system.service.ConfigService;
@@ -174,6 +175,7 @@
         }
 
         OutputQueue.DEVP.offer(MessageFormat.format("銆恵0}銆慬id:{1}] <<<<< 瀹炴椂鏁版嵁鏇存柊鎴愬姛", DateUtils.convert(new Date()), deviceConfig.getDeviceNo()));
+        StationErrLogSupport.sync(deviceConfig, redisUtil, statusList);
 
         if (System.currentTimeMillis() - deviceDataLogTime > deviceLogCollectTime) {
             DeviceDataLog deviceDataLog = new DeviceDataLog();
diff --git a/src/main/java/com/zy/core/thread/impl/v5/StationV5StatusReader.java b/src/main/java/com/zy/core/thread/impl/v5/StationV5StatusReader.java
index 72f2b96..3d532e2 100644
--- a/src/main/java/com/zy/core/thread/impl/v5/StationV5StatusReader.java
+++ b/src/main/java/com/zy/core/thread/impl/v5/StationV5StatusReader.java
@@ -18,6 +18,7 @@
 import com.zy.core.network.ZyStationConnectDriver;
 import com.zy.core.network.entity.ZyStationStatusEntity;
 import com.zy.core.thread.support.RecentStationArrivalTracker;
+import com.zy.core.thread.support.StationErrLogSupport;
 import com.zy.core.utils.DeviceLogRedisKeyBuilder;
 
 import java.text.MessageFormat;
@@ -108,6 +109,7 @@
 
         OutputQueue.DEVP.offer(MessageFormat.format("銆恵0}銆慬id:{1}] <<<<< 瀹炴椂鏁版嵁鏇存柊鎴愬姛",
                 DateUtils.convert(new Date()), deviceConfig.getDeviceNo()));
+        StationErrLogSupport.sync(deviceConfig, redisUtil, statusList);
 
         if (System.currentTimeMillis() - deviceDataLogTime > deviceLogCollectTime) {
             DeviceDataLog deviceDataLog = new DeviceDataLog();
diff --git a/src/main/java/com/zy/core/thread/support/StationErrLogSupport.java b/src/main/java/com/zy/core/thread/support/StationErrLogSupport.java
new file mode 100644
index 0000000..8d34431
--- /dev/null
+++ b/src/main/java/com/zy/core/thread/support/StationErrLogSupport.java
@@ -0,0 +1,138 @@
+package com.zy.core.thread.support;
+
+import com.alibaba.fastjson.JSONObject;
+import com.core.common.Cools;
+import com.core.common.SpringUtils;
+import com.zy.asrs.entity.BasStationErrLog;
+import com.zy.asrs.entity.DeviceConfig;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.BasStationErrLogService;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.common.utils.RedisUtil;
+import com.zy.core.enums.RedisKeyType;
+import com.zy.core.model.protocol.StationProtocol;
+
+import java.util.Date;
+import java.util.List;
+
+public final class StationErrLogSupport {
+
+    private StationErrLogSupport() {
+    }
+
+    public static void sync(DeviceConfig deviceConfig, RedisUtil redisUtil, List<StationProtocol> statusList) {
+        if (redisUtil == null || statusList == null || statusList.isEmpty()) {
+            return;
+        }
+        try {
+            BasStationErrLogService errLogService = SpringUtils.getBean(BasStationErrLogService.class);
+            if (errLogService == null) {
+                return;
+            }
+            WrkMastService wrkMastService = null;
+            try {
+                wrkMastService = SpringUtils.getBean(WrkMastService.class);
+            } catch (Exception ignore) {
+            }
+            for (StationProtocol stationProtocol : statusList) {
+                syncOne(deviceConfig, redisUtil, errLogService, wrkMastService, stationProtocol);
+            }
+        } catch (Exception ignore) {
+        }
+    }
+
+    private static void syncOne(DeviceConfig deviceConfig,
+                                RedisUtil redisUtil,
+                                BasStationErrLogService errLogService,
+                                WrkMastService wrkMastService,
+                                StationProtocol stationProtocol) {
+        if (stationProtocol == null || stationProtocol.getStationId() == null) {
+            return;
+        }
+        String errFlagKey = RedisKeyType.DEVICE_ERR_ACTIVE_STATION.key + stationProtocol.getStationId();
+        Object active = redisUtil.get(errFlagKey);
+        Date now = new Date();
+        if (hasError(stationProtocol)) {
+            if (active != null) {
+                return;
+            }
+            BasStationErrLog log = new BasStationErrLog();
+            Integer wrkNo = stationProtocol.getTaskNo();
+            if (wrkNo != null && wrkNo > 0) {
+                log.setWrkNo(wrkNo);
+            }
+            log.setStartTime(now);
+            log.setStationId(stationProtocol.getStationId());
+            log.setStaNo(stationProtocol.getTargetStaNo());
+            log.setBarcode(stationProtocol.getBarcode());
+            log.setErrCode(normalizeErrCode(stationProtocol.getError()));
+            log.setError(resolveError(stationProtocol));
+            log.setStatus(1);
+            log.setCreateTime(now);
+            log.setSystemStatus(buildSystemStatus(deviceConfig, stationProtocol));
+            fillWorkContext(log, wrkMastService, wrkNo);
+            errLogService.save(log);
+            if (log.getId() != null) {
+                redisUtil.set(errFlagKey, log.getId(), 60 * 60 * 24);
+            }
+            return;
+        }
+
+        if (active == null) {
+            return;
+        }
+        BasStationErrLog update = new BasStationErrLog();
+        update.setId(Long.valueOf(String.valueOf(active)));
+        update.setEndTime(now);
+        update.setStatus(2);
+        update.setUpdateTime(now);
+        errLogService.updateById(update);
+        redisUtil.del(errFlagKey);
+    }
+
+    private static void fillWorkContext(BasStationErrLog log, WrkMastService wrkMastService, Integer wrkNo) {
+        if (wrkMastService == null || wrkNo == null || wrkNo <= 0) {
+            return;
+        }
+        WrkMast wrkMast = wrkMastService.selectByWorkNo(wrkNo);
+        if (wrkMast == null) {
+            return;
+        }
+        log.setWrkSts(wrkMast.getWrkSts());
+        log.setIoType(wrkMast.getIoType());
+        log.setLocNo(wrkMast.getLocNo());
+        log.setSourceStaNo(wrkMast.getSourceStaNo());
+        log.setSourceLocNo(wrkMast.getSourceLocNo());
+        if (log.getStaNo() == null) {
+            log.setStaNo(wrkMast.getStaNo());
+        }
+        if (Cools.isEmpty(log.getBarcode())) {
+            log.setBarcode(wrkMast.getBarcode());
+        }
+    }
+
+    private static boolean hasError(StationProtocol stationProtocol) {
+        return normalizeErrCode(stationProtocol.getError()) != null || !Cools.isEmpty(stationProtocol.getErrorMsg());
+    }
+
+    private static Integer normalizeErrCode(Integer errCode) {
+        return errCode != null && errCode > 0 ? errCode : null;
+    }
+
+    private static String resolveError(StationProtocol stationProtocol) {
+        if (!Cools.isEmpty(stationProtocol.getErrorMsg())) {
+            return stationProtocol.getErrorMsg();
+        }
+        Integer errCode = normalizeErrCode(stationProtocol.getError());
+        return errCode == null ? null : "绔欑偣鎶ヨ";
+    }
+
+    private static String buildSystemStatus(DeviceConfig deviceConfig, StationProtocol stationProtocol) {
+        JSONObject snapshot = new JSONObject();
+        if (deviceConfig != null) {
+            snapshot.put("deviceNo", deviceConfig.getDeviceNo());
+        }
+        snapshot.put("stationProtocol", stationProtocol);
+        return snapshot.toJSONString();
+    }
+}
diff --git a/src/main/resources/i18n/en-US/legacy.properties b/src/main/resources/i18n/en-US/legacy.properties
index 5c33986..68e990a 100644
--- a/src/main/resources/i18n/en-US/legacy.properties
+++ b/src/main/resources/i18n/en-US/legacy.properties
@@ -1028,6 +1028,7 @@
 鍙屽伐浣嶅爢鍨涙満寮傚父鏃ュ織=Dual-station Crane Error Log
 RGV鍛戒护鏃ュ織=RGV Command Log
 RGV寮傚父鏃ュ織=RGV Error Log
+杈撻�佺珯鐐瑰紓甯告棩蹇�=Conveyor Station Error Log
 杈撻�佺珯鐐瑰懡浠ゆ棩蹇�=Conveyor Station Command Log
 绯荤粺閰嶇疆=System Configuration
 鏂板涓夋柟鎺ュ彛缁熻=Add Third-party API Statistics
@@ -1076,6 +1077,8 @@
 淇敼RGV鍛戒护鏃ュ織=Edit RGV Command Log
 鏂板RGV寮傚父鏃ュ織=Add RGV Error Log
 淇敼RGV寮傚父鏃ュ織=Edit RGV Error Log
+鏂板杈撻�佺珯鐐瑰紓甯告棩蹇�=Add Conveyor Station Error Log
+淇敼杈撻�佺珯鐐瑰紓甯告棩蹇�=Edit Conveyor Station Error Log
 鏂板杈撻�佺珯鐐瑰懡浠ゆ棩蹇�=Add Conveyor Station Command Log
 淇敼杈撻�佺珯鐐瑰懡浠ゆ棩蹇�=Edit Conveyor Station Command Log
 鏂板绯荤粺閰嶇疆=Add System Configuration
diff --git a/src/main/resources/i18n/zh-CN/legacy.properties b/src/main/resources/i18n/zh-CN/legacy.properties
index ff78c24..fd1c429 100644
--- a/src/main/resources/i18n/zh-CN/legacy.properties
+++ b/src/main/resources/i18n/zh-CN/legacy.properties
@@ -108,6 +108,7 @@
 鍙屽伐浣嶅爢鍨涙満寮傚父鏃ュ織=鍙屽伐浣嶅爢鍨涙満寮傚父鏃ュ織
 RGV鍛戒护鏃ュ織=RGV鍛戒护鏃ュ織
 RGV寮傚父鏃ュ織=RGV寮傚父鏃ュ織
+杈撻�佺珯鐐瑰紓甯告棩蹇�=杈撻�佺珯鐐瑰紓甯告棩蹇�
 杈撻�佺珯鐐瑰懡浠ゆ棩蹇�=杈撻�佺珯鐐瑰懡浠ゆ棩蹇�
 绯荤粺閰嶇疆=绯荤粺閰嶇疆
 鏂板涓夋柟鎺ュ彛缁熻=鏂板涓夋柟鎺ュ彛缁熻
@@ -156,6 +157,8 @@
 淇敼RGV鍛戒护鏃ュ織=淇敼RGV鍛戒护鏃ュ織
 鏂板RGV寮傚父鏃ュ織=鏂板RGV寮傚父鏃ュ織
 淇敼RGV寮傚父鏃ュ織=淇敼RGV寮傚父鏃ュ織
+鏂板杈撻�佺珯鐐瑰紓甯告棩蹇�=鏂板杈撻�佺珯鐐瑰紓甯告棩蹇�
+淇敼杈撻�佺珯鐐瑰紓甯告棩蹇�=淇敼杈撻�佺珯鐐瑰紓甯告棩蹇�
 鏂板杈撻�佺珯鐐瑰懡浠ゆ棩蹇�=鏂板杈撻�佺珯鐐瑰懡浠ゆ棩蹇�
 淇敼杈撻�佺珯鐐瑰懡浠ゆ棩蹇�=淇敼杈撻�佺珯鐐瑰懡浠ゆ棩蹇�
 鏂板绯荤粺閰嶇疆=鏂板绯荤粺閰嶇疆
diff --git a/src/main/resources/mapper/BasStationErrLogMapper.xml b/src/main/resources/mapper/BasStationErrLogMapper.xml
new file mode 100644
index 0000000..697312b
--- /dev/null
+++ b/src/main/resources/mapper/BasStationErrLogMapper.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zy.asrs.mapper.BasStationErrLogMapper">
+
+    <resultMap id="BaseResultMap" type="com.zy.asrs.entity.BasStationErrLog">
+        <id column="id" property="id" />
+        <result column="wrk_no" property="wrkNo" />
+        <result column="start_time" property="startTime" />
+        <result column="end_time" property="endTime" />
+        <result column="wrk_sts" property="wrkSts" />
+        <result column="io_type" property="ioType" />
+        <result column="station_id" property="stationId" />
+        <result column="loc_no" property="locNo" />
+        <result column="sta_no" property="staNo" />
+        <result column="source_sta_no" property="sourceStaNo" />
+        <result column="source_loc_no" property="sourceLocNo" />
+        <result column="barcode" property="barcode" />
+        <result column="err_code" property="errCode" />
+        <result column="error" property="error" />
+        <result column="status" property="status" />
+        <result column="create_time" property="createTime" />
+        <result column="create_by" property="createBy" />
+        <result column="update_time" property="updateTime" />
+        <result column="update_by" property="updateBy" />
+        <result column="memo" property="memo" />
+        <result column="system_status" property="systemStatus" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/sql/20260331_add_station_err_log.sql b/src/main/resources/sql/20260331_add_station_err_log.sql
new file mode 100644
index 0000000..be5f1fd
--- /dev/null
+++ b/src/main/resources/sql/20260331_add_station_err_log.sql
@@ -0,0 +1,172 @@
+CREATE TABLE IF NOT EXISTS `asr_bas_station_err_log` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '缂栧彿',
+  `wrk_no` int DEFAULT NULL COMMENT '宸ヤ綔鍙�',
+  `start_time` datetime DEFAULT NULL COMMENT '鍙戠敓鏃堕棿',
+  `end_time` datetime DEFAULT NULL COMMENT '缁撴潫鏃堕棿',
+  `wrk_sts` bigint DEFAULT NULL COMMENT '宸ヤ綔鐘舵��',
+  `io_type` int DEFAULT NULL COMMENT '鍏ュ嚭搴撶被鍨�',
+  `station_id` int DEFAULT NULL COMMENT '绔欑偣鍙�',
+  `loc_no` varchar(64) DEFAULT NULL COMMENT '鐩爣搴撲綅',
+  `sta_no` int DEFAULT NULL COMMENT '鐩爣绔�',
+  `source_sta_no` int DEFAULT NULL COMMENT '婧愮珯',
+  `source_loc_no` varchar(64) DEFAULT NULL COMMENT '婧愬簱浣�',
+  `barcode` varchar(128) DEFAULT NULL COMMENT '鏉$爜',
+  `err_code` int DEFAULT NULL COMMENT '寮傚父鐮�',
+  `error` text COMMENT '寮傚父鎻忚堪',
+  `status` int DEFAULT NULL COMMENT '寮傚父鎯呭喌 1鏈鐞� 2宸蹭慨澶�',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `create_by` bigint DEFAULT NULL COMMENT '鍒涘缓浜�',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `update_by` bigint DEFAULT NULL COMMENT '鏇存柊浜�',
+  `memo` varchar(255) DEFAULT NULL COMMENT '澶囨敞',
+  `system_status` longtext COMMENT '绯荤粺鐘舵�佸揩鐓�',
+  PRIMARY KEY (`id`),
+  KEY `idx_station_err_log_wrk_no` (`wrk_no`),
+  KEY `idx_station_err_log_station_id` (`station_id`),
+  KEY `idx_station_err_log_status` (`status`),
+  KEY `idx_station_err_log_start_time` (`start_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='杈撻�佺珯鐐瑰紓甯告棩蹇�';
+
+SET @wrk_analysis_table_exists := (
+  SELECT COUNT(*)
+  FROM `information_schema`.`TABLES`
+  WHERE `TABLE_SCHEMA` = DATABASE()
+    AND `TABLE_NAME` = 'asr_wrk_analysis'
+);
+
+SET @station_fault_count_exists := (
+  SELECT COUNT(*)
+  FROM `information_schema`.`COLUMNS`
+  WHERE `TABLE_SCHEMA` = DATABASE()
+    AND `TABLE_NAME` = 'asr_wrk_analysis'
+    AND `COLUMN_NAME` = 'station_fault_count'
+);
+
+SET @station_fault_duration_exists := (
+  SELECT COUNT(*)
+  FROM `information_schema`.`COLUMNS`
+  WHERE `TABLE_SCHEMA` = DATABASE()
+    AND `TABLE_NAME` = 'asr_wrk_analysis'
+    AND `COLUMN_NAME` = 'station_fault_duration_ms'
+);
+
+SET @sql_station_fault_count := IF(
+  @wrk_analysis_table_exists = 1 AND @station_fault_count_exists = 0,
+  'ALTER TABLE `asr_wrk_analysis` ADD COLUMN `station_fault_count` int DEFAULT 0 COMMENT ''杈撻�佺珯鐐规晠闅滄鏁�'' AFTER `rgv_fault_duration_ms`',
+  'SELECT 1'
+);
+PREPARE stmt_station_fault_count FROM @sql_station_fault_count;
+EXECUTE stmt_station_fault_count;
+DEALLOCATE PREPARE stmt_station_fault_count;
+
+SET @sql_station_fault_duration := IF(
+  @wrk_analysis_table_exists = 1 AND @station_fault_duration_exists = 0,
+  'ALTER TABLE `asr_wrk_analysis` ADD COLUMN `station_fault_duration_ms` bigint DEFAULT 0 COMMENT ''杈撻�佺珯鐐规晠闅滆�楁椂姣'' AFTER `station_fault_count`',
+  'SELECT 1'
+);
+PREPARE stmt_station_fault_duration FROM @sql_station_fault_duration;
+EXECUTE stmt_station_fault_duration;
+DEALLOCATE PREPARE stmt_station_fault_duration;
+
+UPDATE `asr_wrk_analysis` a
+LEFT JOIN (
+    SELECT
+        e.`wrk_no`,
+        COUNT(*) AS `fault_count`,
+        SUM(
+            CASE
+                WHEN e.`start_time` IS NULL THEN 0
+                ELSE GREATEST(
+                    TIMESTAMPDIFF(
+                        MICROSECOND,
+                        e.`start_time`,
+                        COALESCE(e.`end_time`, wl.`modi_time`, wl.`io_time`, wl.`appe_time`, e.`start_time`)
+                    ),
+                    0
+                ) DIV 1000
+            END
+        ) AS `fault_duration_ms`
+    FROM `asr_bas_station_err_log` e
+    LEFT JOIN `asr_wrk_mast_log` wl ON wl.`wrk_no` = e.`wrk_no`
+    GROUP BY e.`wrk_no`
+) s ON s.`wrk_no` = a.`wrk_no`
+SET a.`station_fault_count` = COALESCE(s.`fault_count`, 0),
+    a.`station_fault_duration_ms` = COALESCE(s.`fault_duration_ms`, 0),
+    a.`fault_count` = COALESCE(a.`crn_fault_count`, 0)
+        + COALESCE(a.`dual_crn_fault_count`, 0)
+        + COALESCE(a.`rgv_fault_count`, 0)
+        + COALESCE(s.`fault_count`, 0),
+    a.`fault_duration_ms` = COALESCE(a.`crn_fault_duration_ms`, 0)
+        + COALESCE(a.`dual_crn_fault_duration_ms`, 0)
+        + COALESCE(a.`rgv_fault_duration_ms`, 0)
+        + COALESCE(s.`fault_duration_ms`, 0),
+    a.`has_fault` = CASE
+        WHEN COALESCE(a.`crn_fault_count`, 0)
+            + COALESCE(a.`dual_crn_fault_count`, 0)
+            + COALESCE(a.`rgv_fault_count`, 0)
+            + COALESCE(s.`fault_count`, 0) > 0 THEN 1
+        ELSE 0
+    END,
+    a.`update_time` = NOW()
+WHERE @wrk_analysis_table_exists = 1;
+
+SET @station_err_log_parent_id := COALESCE(
+  (
+    SELECT `id`
+    FROM `sys_resource`
+    WHERE `code` = 'logReport' AND `level` = 1
+    ORDER BY `id`
+    LIMIT 1
+  ),
+  (
+    SELECT `id`
+    FROM `sys_resource`
+    WHERE `code` = 'develop' AND `level` = 1
+    ORDER BY `id`
+    LIMIT 1
+  )
+);
+
+INSERT INTO `sys_resource`(`code`, `name`, `resource_id`, `level`, `sort`, `status`)
+SELECT 'basStationErrLog/basStationErrLog.html', '杈撻�佺珯鐐瑰紓甯告棩蹇�', @station_err_log_parent_id, 2, 995, 1
+FROM dual
+WHERE @station_err_log_parent_id IS NOT NULL
+  AND NOT EXISTS (
+    SELECT 1
+    FROM `sys_resource`
+    WHERE `code` = 'basStationErrLog/basStationErrLog.html' AND `level` = 2
+  );
+
+UPDATE `sys_resource`
+SET `name` = '杈撻�佺珯鐐瑰紓甯告棩蹇�',
+    `resource_id` = @station_err_log_parent_id,
+    `level` = 2,
+    `sort` = 995,
+    `status` = 1
+WHERE `code` = 'basStationErrLog/basStationErrLog.html' AND `level` = 2;
+
+SET @station_err_log_id := (
+  SELECT `id`
+  FROM `sys_resource`
+  WHERE `code` = 'basStationErrLog/basStationErrLog.html' AND `level` = 2
+  ORDER BY `id`
+  LIMIT 1
+);
+
+INSERT INTO `sys_resource`(`code`, `name`, `resource_id`, `level`, `sort`, `status`)
+SELECT 'basStationErrLog/basStationErrLog.html#view', '鏌ョ湅', @station_err_log_id, 3, 1, 1
+FROM dual
+WHERE @station_err_log_id IS NOT NULL
+  AND NOT EXISTS (
+    SELECT 1
+    FROM `sys_resource`
+    WHERE `code` = 'basStationErrLog/basStationErrLog.html#view' AND `level` = 3
+  );
+
+UPDATE `sys_resource`
+SET `name` = '鏌ョ湅',
+    `resource_id` = @station_err_log_id,
+    `level` = 3,
+    `sort` = 1,
+    `status` = 1
+WHERE `code` = 'basStationErrLog/basStationErrLog.html#view' AND `level` = 3;
diff --git a/src/main/webapp/static/js/basStationErrLog/basStationErrLog.js b/src/main/webapp/static/js/basStationErrLog/basStationErrLog.js
new file mode 100644
index 0000000..86a95eb
--- /dev/null
+++ b/src/main/webapp/static/js/basStationErrLog/basStationErrLog.js
@@ -0,0 +1,1165 @@
+(function () {
+    var simpleEntityName = 'basStationErrLog';
+    var entityName = 'BasStationErrLog';
+    var primaryKeyField = 'id';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'id',
+        columnName: 'id',
+        label: '缂栥��銆�鍙�',
+        tableProp: 'id',
+        exportField: 'id',
+        kind: 'text',
+        valueType: 'number',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wrkNo',
+        columnName: 'wrk_no',
+        label: '宸� 浣� 鍙�',
+        tableProp: 'wrkNo',
+        exportField: 'wrkNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'startTime',
+        columnName: 'start_time',
+        label: '鍙戠敓鏃堕棿',
+        tableProp: 'startTime$',
+        exportField: 'startTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'endTime',
+        columnName: 'end_time',
+        label: '缁撴潫鏃堕棿',
+        tableProp: 'endTime$',
+        exportField: 'endTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'wrkSts',
+        columnName: 'wrk_sts',
+        label: '宸ヤ綔鐘舵��',
+        tableProp: 'wrkSts$',
+        exportField: 'wrkSts$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'basWrkStatus',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioType',
+        columnName: 'io_type',
+        label: '鍏ュ嚭搴撶被鍨�',
+        tableProp: 'ioType$',
+        exportField: 'ioType$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: 'basWrkIotype',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'stationId',
+        columnName: 'station_id',
+        label: '杈撻�佺珯鐐�',
+        tableProp: 'stationLabel$',
+        exportField: 'stationLabel$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 130,
+        enumOptions: [],
+        foreignQuery: 'basStation',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locNo',
+        columnName: 'loc_no',
+        label: '鐩爣搴撲綅',
+        tableProp: 'locNo',
+        exportField: 'locNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'staNo',
+        columnName: 'sta_no',
+        label: '鐩� 鏍� 绔�',
+        tableProp: 'staNo',
+        exportField: 'staNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'sourceStaNo',
+        columnName: 'source_sta_no',
+        label: '婧愩��銆�绔�',
+        tableProp: 'sourceStaNo',
+        exportField: 'sourceStaNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'sourceLocNo',
+        columnName: 'source_loc_no',
+        label: '婧� 搴� 浣�',
+        tableProp: 'sourceLocNo',
+        exportField: 'sourceLocNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'barcode',
+        columnName: 'barcode',
+        label: '鏉°��銆�鐮�',
+        tableProp: 'barcode',
+        exportField: 'barcode',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errCode',
+        columnName: 'err_code',
+        label: '寮� 甯� 鐮�',
+        tableProp: 'errCode',
+        exportField: 'errCode',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'error',
+        columnName: 'error',
+        label: '寮傘��銆�甯�',
+        tableProp: 'error',
+        exportField: 'error',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '寮傚父鎯呭喌',
+        tableProp: 'status$',
+        exportField: 'status$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '1', label: '鏈鐞�' }, { rawValue: '2', label: '宸蹭慨澶�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'createTime',
+        columnName: 'create_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'createTime$',
+        exportField: 'createTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'createBy',
+        columnName: 'create_by',
+        label: '娣诲姞浜哄憳',
+        tableProp: 'createBy$',
+        exportField: 'createBy$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'user',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'updateTime',
+        columnName: 'update_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'updateTime$',
+        exportField: 'updateTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'updateBy',
+        columnName: 'update_by',
+        label: '淇敼浜哄憳',
+        tableProp: 'updateBy$',
+        exportField: 'updateBy$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'user',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'memo',
+        columnName: 'memo',
+        label: '澶囥��銆�娉�',
+        tableProp: 'memo',
+        exportField: 'memo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'systemStatus',
+        columnName: 'system_status',
+        label: '绯荤粺鐘舵�佹暟鎹�',
+        tableProp: 'systemStatus',
+        exportField: 'systemStatus',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 134,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
+
+    ]);
+
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
+        }
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
+        }
+        raw = String(raw)
+            .replace(/\$/g, '')
+            .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
+            .replace(/_/g, ' ')
+            .replace(/\s+/g, ' ')
+            .trim();
+        return raw.replace(/\b[a-z]/g, function (letter) {
+            return letter.toUpperCase();
+        });
+    }
+
+    function dedupeFieldMeta(list) {
+        var result = [];
+        var seen = {};
+        (list || []).forEach(function (field) {
+            if (!field || !field.field || seen[field.field]) {
+                return;
+            }
+            field.label = formatFieldLabel(field);
+            seen[field.field] = true;
+            result.push(field);
+        });
+        return result;
+    }
+
+    function isEmptyValue(value) {
+        return value === null || value === undefined || value === '';
+    }
+
+    function stringValue(value) {
+        return isEmptyValue(value) ? '' : String(value);
+    }
+
+    function valueOrDash(value) {
+        return isEmptyValue(value) ? '--' : value;
+    }
+
+    function normalizeOptionValue(field, rawValue) {
+        if (rawValue === null || rawValue === undefined) {
+            return null;
+        }
+        if (rawValue === '') {
+            return '';
+        }
+        if (field && field.valueType === 'number') {
+            var numberVal = Number(rawValue);
+            return isNaN(numberVal) ? rawValue : numberVal;
+        }
+        return String(rawValue);
+    }
+
+    function isSearchableField(field) {
+        return !!field && field.kind !== 'image' && !field.textarea;
+    }
+
+    function isSortableField(field) {
+        if (!field) {
+            return false;
+        }
+        if (field.primaryKey) {
+            return true;
+        }
+        return field.kind !== 'image' && !field.textarea && field.kind !== 'foreign';
+    }
+
+    function defaultFieldValue(field) {
+        if (field.primaryKey) {
+            return null;
+        }
+        if (field.kind === 'checkbox') {
+            return normalizeOptionValue(field, field.checkboxInactiveRaw);
+        }
+        return '';
+    }
+
+    function defaultSearchFieldValue(field) {
+        if (field.kind === 'date') {
+            return [];
+        }
+        if (field.kind === 'enum' || field.kind === 'checkbox') {
+            return null;
+        }
+        return '';
+    }
+
+    function createSearchDefaults() {
+        var result = {
+            condition: ''
+        };
+        fieldMeta.forEach(function (field) {
+            if (!isSearchableField(field)) {
+                return;
+            }
+            result[field.field] = defaultSearchFieldValue(field);
+        });
+        return result;
+    }
+
+    function createSearchDisplayDefaults() {
+        var result = {};
+        fieldMeta.forEach(function (field) {
+            if (field.kind === 'foreign' && isSearchableField(field)) {
+                result[field.field] = '';
+            }
+        });
+        return result;
+    }
+
+    function createDefaultVisibleColumnKeys() {
+        return fieldMeta.map(function (field) {
+            return field.field;
+        });
+    }
+
+    function createFormDefaults() {
+        var result = {};
+        fieldMeta.forEach(function (field) {
+            result[field.field] = defaultFieldValue(field);
+        });
+        return result;
+    }
+
+    function createDisplayDefaults() {
+        var result = {};
+        fieldMeta.forEach(function (field) {
+            if (field.kind === 'foreign') {
+                result[field.field] = '';
+            }
+        });
+        return result;
+    }
+
+    function createFormRules() {
+        var rules = {};
+        fieldMeta.forEach(function (field) {
+            if (field.primaryKey || !field.required) {
+                return;
+            }
+            rules[field.field] = [{
+                required: true,
+                message: (field.kind === 'date' || field.kind === 'enum' ? '璇烽�夋嫨' : '璇疯緭鍏�') + field.label,
+                trigger: (field.kind === 'date' || field.kind === 'enum') ? 'change' : 'blur'
+            }];
+        });
+        return rules;
+    }
+
+    function getTableValue(row, field) {
+        var prop = field.tableProp || field.field;
+        if (row && !isEmptyValue(row[prop])) {
+            return row[prop];
+        }
+        return row ? row[field.field] : '';
+    }
+
+    function isCheckboxChecked(row, field) {
+        var value = row ? row[field.field] : null;
+        var activeValue = normalizeOptionValue(field, field.checkboxActiveRaw);
+        return String(value) === String(activeValue);
+    }
+
+    function exportCell(value) {
+        return stringValue(value).replace(/\t/g, ' ').replace(/\r?\n/g, ' ');
+    }
+
+    function escapeHtml(value) {
+        return exportCell(value)
+            .replace(/&/g, '&amp;')
+            .replace(/</g, '&lt;')
+            .replace(/>/g, '&gt;')
+            .replace(/"/g, '&quot;')
+            .replace(/'/g, '&#39;');
+    }
+
+    function buildPayload(form) {
+        var payload = {};
+        fieldMeta.forEach(function (field) {
+            var value = form[field.field];
+            if (field.primaryKey) {
+                if (!isEmptyValue(value)) {
+                    payload[field.field] = value;
+                }
+                return;
+            }
+            if (field.kind === 'foreign' && isEmptyValue(value)) {
+                value = null;
+            }
+            if (field.kind === 'enum' && value === '') {
+                value = null;
+            }
+            if (field.kind === 'checkbox' && isEmptyValue(value)) {
+                value = normalizeOptionValue(field, field.checkboxInactiveRaw);
+            }
+            if (field.valueType === 'number' && !isEmptyValue(value)) {
+                value = Number(value);
+            }
+            if (field.valueType === 'number' && value === '') {
+                value = null;
+            }
+            payload[field.field] = value;
+        });
+        return payload;
+    }
+
+    function fillFormFromRow(row, form, display) {
+        fieldMeta.forEach(function (field) {
+            if (field.primaryKey) {
+                form[field.field] = row[field.field];
+                return;
+            }
+            if (field.kind === 'date') {
+                form[field.field] = row[field.tableProp] || row[field.field] || '';
+                return;
+            }
+            if (field.kind === 'foreign') {
+                form[field.field] = isEmptyValue(row[field.field]) ? '' : normalizeOptionValue(field, row[field.field]);
+                if (display) {
+                    display[field.field] = row[field.tableProp] || (isEmptyValue(row[field.field]) ? '' : String(row[field.field]));
+                }
+                return;
+            }
+            if (field.kind === 'enum') {
+                form[field.field] = isEmptyValue(row[field.field]) ? '' : normalizeOptionValue(field, row[field.field]);
+                return;
+            }
+            if (field.kind === 'checkbox') {
+                form[field.field] = isEmptyValue(row[field.field])
+                    ? normalizeOptionValue(field, field.checkboxInactiveRaw)
+                    : normalizeOptionValue(field, row[field.field]);
+                return;
+            }
+            form[field.field] = isEmptyValue(row[field.field])
+                ? ''
+                : (field.valueType === 'number' ? String(row[field.field]) : row[field.field]);
+        });
+    }
+
+    function resolveSearchParam(field) {
+        if (field.kind === 'date' && field.columnName) {
+            return field.columnName;
+        }
+        return field.field;
+    }
+
+    function createDownloadFile(filename, titles, rows) {
+        var html = [
+            '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">',
+            '<head><meta charset="UTF-8"></head><body><table border="1"><thead><tr>',
+            titles.map(function (title) {
+                return '<th>' + escapeHtml(title) + '</th>';
+            }).join(''),
+            '</tr></thead><tbody>',
+            (rows || []).map(function (row) {
+                return '<tr>' + (row || []).map(function (value) {
+                    return '<td style="mso-number-format:\\@;">' + escapeHtml(value) + '</td>';
+                }).join('') + '</tr>';
+            }).join(''),
+            '</tbody></table></body></html>'
+        ].join('');
+        var blob = new Blob(['\ufeff' + html], {
+            type: 'application/vnd.ms-excel;charset=utf-8;'
+        });
+        var anchor = document.createElement('a');
+        anchor.href = URL.createObjectURL(blob);
+        anchor.download = filename;
+        document.body.appendChild(anchor);
+        anchor.click();
+        setTimeout(function () {
+            URL.revokeObjectURL(anchor.href);
+            document.body.removeChild(anchor);
+        }, 0);
+    }
+
+    function buildTimestamp() {
+        var now = new Date();
+        var pad = function (num) {
+            return num < 10 ? '0' + num : String(num);
+        };
+        return now.getFullYear()
+            + pad(now.getMonth() + 1)
+            + pad(now.getDate())
+            + '_'
+            + pad(now.getHours())
+            + pad(now.getMinutes())
+            + pad(now.getSeconds());
+    }
+
+    function authHeaders() {
+        return {
+            token: localStorage.getItem('token')
+        };
+    }
+
+    function handleForbidden(res) {
+        if (res && res.code === 403) {
+            top.location.href = baseUrl + '/';
+            return true;
+        }
+        return false;
+    }
+
+    var sharedMethods = {
+        authHeaders: authHeaders,
+        handleForbidden: handleForbidden,
+        valueOrDash: valueOrDash,
+        stringValue: stringValue,
+        getTableValue: getTableValue,
+        isCheckboxChecked: isCheckboxChecked,
+        normalizeOptionValue: normalizeOptionValue,
+        isSortableField: isSortableField,
+        getSuggestionFetcher: function (field) {
+            var self = this;
+            return function (queryString, callback) {
+                self.fetchForeignSuggestions(field, queryString, callback);
+            };
+        },
+        fetchForeignSuggestions: function (field, queryString, callback) {
+            if (!field.foreignQuery || !queryString) {
+                callback([]);
+                return;
+            }
+            var self = this;
+            $.ajax({
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
+                success: function (res) {
+                    if (self.handleForbidden(res)) {
+                        return;
+                    }
+                    if (!res || res.code !== 200 || !Array.isArray(res.data)) {
+                        callback([]);
+                        return;
+                    }
+                    callback(res.data.map(function (item) {
+                        return {
+                            id: item.id,
+                            value: item.value
+                        };
+                    }));
+                },
+                error: function () {
+                    callback([]);
+                }
+            });
+        },
+        handleForeignSelect: function (field, item) {
+            this.$set(this.displayTarget, field.field, item && item.value ? item.value : '');
+            this.$set(this.formTarget, field.field, item && item.id !== undefined ? normalizeOptionValue(field, item.id) : '');
+        },
+        handleForeignInput: function (field) {
+            if (!this.displayTarget || !this.formTarget) {
+                return;
+            }
+            if (this.displayTarget[field.field]) {
+                return;
+            }
+            this.$set(this.formTarget, field.field, '');
+        }
+    };
+
+    if (document.getElementById('app')) {
+        new Vue({
+            el: '#app',
+            data: function () {
+                return {
+                    fieldMeta: fieldMeta,
+                    primaryKeyField: primaryKeyField,
+                    loading: false,
+                    exporting: false,
+                    tableData: [],
+                    selection: [],
+                    advancedFiltersVisible: false,
+                    allColumns: fieldMeta.slice(),
+                    visibleColumnKeys: createDefaultVisibleColumnKeys(),
+                    searchForm: createSearchDefaults(),
+                    searchDisplay: createSearchDisplayDefaults(),
+                    page: {
+                        curr: 1,
+                        limit: 15,
+                        total: 0
+                    },
+                    sortState: {
+                        prop: '',
+                        order: ''
+                    },
+                    dialog: {
+                        visible: false,
+                        mode: 'create',
+                        submitting: false
+                    },
+                    layoutTimer: null,
+                    tableResizeHandler: null,
+                    dialogForm: createFormDefaults(),
+                    dialogDisplay: createDisplayDefaults(),
+                    dialogRules: createFormRules()
+                };
+            },
+            computed: {
+                searchableFields: function () {
+                    return this.fieldMeta.filter(function (field) {
+                        return isSearchableField(field);
+                    });
+                },
+                quickSearchableFields: function () {
+                    var result = [];
+                    this.searchableFields.forEach(function (field) {
+                        if (result.length >= 3 || field.kind === 'date') {
+                            return;
+                        }
+                        result.push(field);
+                    });
+                    return result;
+                },
+                advancedSearchableFields: function () {
+                    var quickKeys = this.quickSearchableFields.map(function (field) {
+                        return field.field;
+                    });
+                    return this.searchableFields.filter(function (field) {
+                        return quickKeys.indexOf(field.field) === -1;
+                    });
+                },
+                hasAdvancedFilters: function () {
+                    return this.advancedSearchableFields.length > 0;
+                },
+                visibleColumns: function () {
+                    var keys = this.visibleColumnKeys;
+                    return this.allColumns.filter(function (field) {
+                        return keys.indexOf(field.field) !== -1;
+                    });
+                },
+                editableFields: function () {
+                    return this.fieldMeta.filter(function (field) {
+                        return !field.primaryKey;
+                    });
+                },
+                exportColumns: function () {
+                    return this.visibleColumns.map(function (field) {
+                        return {
+                            field: field.exportField || field.tableProp || field.field,
+                            label: field.label
+                        };
+                    });
+                },
+                tableHeight: function () {
+                    return this.advancedFiltersVisible && this.hasAdvancedFilters
+                        ? 'calc(100vh - 390px)'
+                        : 'calc(100vh - 300px)';
+                },
+                formTarget: function () {
+                    return this.dialogForm;
+                },
+                displayTarget: function () {
+                    return this.dialogDisplay;
+                }
+            },
+            created: function () {
+                this.loadTable();
+            },
+            mounted: function () {
+                var self = this;
+                self.requestTableLayout(80);
+                self.tableResizeHandler = function () {
+                    self.requestTableLayout(80);
+                };
+                window.addEventListener('resize', self.tableResizeHandler);
+            },
+            beforeDestroy: function () {
+                if (this.layoutTimer) {
+                    clearTimeout(this.layoutTimer);
+                    this.layoutTimer = null;
+                }
+                if (this.tableResizeHandler) {
+                    window.removeEventListener('resize', this.tableResizeHandler);
+                    this.tableResizeHandler = null;
+                }
+            },
+            methods: $.extend({}, sharedMethods, {
+                requestTableLayout: function (delay) {
+                    var self = this;
+                    if (self.layoutTimer) {
+                        clearTimeout(self.layoutTimer);
+                    }
+                    self.$nextTick(function () {
+                        self.layoutTimer = setTimeout(function () {
+                            var table = self.$refs.dataTable;
+                            if (table && typeof table.doLayout === 'function') {
+                                table.doLayout();
+                            }
+                        }, delay || 40);
+                    });
+                },
+                isColumnVisible: function (fieldName) {
+                    return this.visibleColumnKeys.indexOf(fieldName) !== -1;
+                },
+                toggleColumn: function (fieldName, visible) {
+                    if (visible) {
+                        if (this.visibleColumnKeys.indexOf(fieldName) === -1) {
+                            this.visibleColumnKeys.push(fieldName);
+                        }
+                        this.requestTableLayout(80);
+                        return;
+                    }
+                    if (this.visibleColumnKeys.length === 1) {
+                        this.$message.warning('鑷冲皯淇濈暀涓�鍒�');
+                        return;
+                    }
+                    this.visibleColumnKeys = this.visibleColumnKeys.filter(function (item) {
+                        return item !== fieldName;
+                    });
+                    this.requestTableLayout(80);
+                },
+                selectAllColumns: function () {
+                    this.visibleColumnKeys = createDefaultVisibleColumnKeys();
+                    this.requestTableLayout(80);
+                },
+                resetColumns: function () {
+                    this.visibleColumnKeys = createDefaultVisibleColumnKeys();
+                    this.requestTableLayout(80);
+                },
+                toggleAdvancedFilters: function () {
+                    this.advancedFiltersVisible = !this.advancedFiltersVisible;
+                    this.requestTableLayout(260);
+                },
+                handleSearchForeignSelect: function (field, item) {
+                    this.$set(this.searchDisplay, field.field, item && item.value ? item.value : '');
+                    this.$set(this.searchForm, field.field, item && item.id !== undefined ? normalizeOptionValue(field, item.id) : '');
+                },
+                handleSearchForeignInput: function (field) {
+                    if (this.searchDisplay[field.field]) {
+                        return;
+                    }
+                    this.$set(this.searchForm, field.field, '');
+                },
+                buildQueryParams: function () {
+                    var self = this;
+                    var params = {
+                        curr: self.page.curr,
+                        limit: self.page.limit
+                    };
+                    if (self.searchForm.condition) {
+                        params.condition = self.searchForm.condition;
+                    }
+                    self.searchableFields.forEach(function (field) {
+                        var value = self.searchForm[field.field];
+                        if (field.kind === 'date') {
+                            if (value && value.length === 2) {
+                                params[resolveSearchParam(field)] = value[0] + ' - ' + value[1];
+                            }
+                            return;
+                        }
+                        if (!isEmptyValue(value)) {
+                            params[resolveSearchParam(field)] = value;
+                        }
+                    });
+                    if (self.sortState.prop && self.sortState.order) {
+                        params.orderByField = self.sortState.prop;
+                        params.orderByType = self.sortState.order === 'ascending' ? 'asc' : 'desc';
+                    }
+                    return params;
+                },
+                loadTable: function () {
+                    var self = this;
+                    self.loading = true;
+                    $.ajax({
+                        url: baseUrl + '/' + simpleEntityName + '/list/auth',
+                        method: 'GET',
+                        headers: self.authHeaders(),
+                        data: self.buildQueryParams(),
+                        success: function (res) {
+                            self.loading = false;
+                            if (self.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!res || res.code !== 200) {
+                                self.$message.error((res && res.msg) ? res.msg : '鍔犺浇澶辫触');
+                                return;
+                            }
+                            var payload = res.data || {};
+                            self.tableData = Array.isArray(payload.records) ? payload.records : [];
+                            self.page.total = payload.total || 0;
+                            self.requestTableLayout(80);
+                        },
+                        error: function () {
+                            self.loading = false;
+                            self.requestTableLayout(80);
+                            self.$message.error('鍔犺浇澶辫触');
+                        }
+                    });
+                },
+                handleSearch: function () {
+                    this.page.curr = 1;
+                    this.loadTable();
+                },
+                handleReset: function () {
+                    this.searchForm = createSearchDefaults();
+                    this.searchDisplay = createSearchDisplayDefaults();
+                    this.advancedFiltersVisible = false;
+                    this.page.curr = 1;
+                    this.sortState = {
+                        prop: '',
+                        order: ''
+                    };
+                    this.loadTable();
+                },
+                handleSelectionChange: function (rows) {
+                    this.selection = rows || [];
+                },
+                handleSortChange: function (payload) {
+                    this.sortState = {
+                        prop: payload && payload.prop ? payload.prop : '',
+                        order: payload && payload.order ? payload.order : ''
+                    };
+                    this.page.curr = 1;
+                    this.loadTable();
+                },
+                handleCurrentChange: function (curr) {
+                    this.page.curr = curr;
+                    this.loadTable();
+                },
+                handleSizeChange: function (limit) {
+                    this.page.limit = limit;
+                    this.page.curr = 1;
+                    this.loadTable();
+                },
+                resetDialogState: function () {
+                    this.dialogForm = createFormDefaults();
+                    this.dialogDisplay = createDisplayDefaults();
+                    if (this.$refs.dialogForm) {
+                        this.$refs.dialogForm.clearValidate();
+                    }
+                },
+                openCreateDialog: function () {
+                    this.dialog.mode = 'create';
+                    this.dialog.visible = true;
+                    this.$nextTick(this.resetDialogState);
+                },
+                openEditDialog: function (row) {
+                    var self = this;
+                    self.dialog.mode = 'edit';
+                    self.dialog.visible = true;
+                    self.$nextTick(function () {
+                        self.resetDialogState();
+                        fillFormFromRow(row, self.dialogForm, self.dialogDisplay);
+                        if (self.$refs.dialogForm) {
+                            self.$refs.dialogForm.clearValidate();
+                        }
+                    });
+                },
+                submitDialog: function () {
+                    var self = this;
+                    if (!self.$refs.dialogForm) {
+                        return;
+                    }
+                    self.$refs.dialogForm.validate(function (valid) {
+                        if (!valid) {
+                            return false;
+                        }
+                        self.dialog.submitting = true;
+                        $.ajax({
+                            url: baseUrl + '/' + simpleEntityName + '/' + (self.dialog.mode === 'create' ? 'add' : 'update') + '/auth',
+                            method: 'POST',
+                            headers: self.authHeaders(),
+                            data: buildPayload(self.dialogForm),
+                            success: function (res) {
+                                self.dialog.submitting = false;
+                                if (self.handleForbidden(res)) {
+                                    return;
+                                }
+                                if (!res || res.code !== 200) {
+                                    self.$message.error((res && res.msg) ? res.msg : '淇濆瓨澶辫触');
+                                    return;
+                                }
+                                self.$message.success(res.msg || '淇濆瓨鎴愬姛');
+                                self.dialog.visible = false;
+                                self.loadTable();
+                            },
+                            error: function () {
+                                self.dialog.submitting = false;
+                                self.$message.error('淇濆瓨澶辫触');
+                            }
+                        });
+                        return true;
+                    });
+                },
+                removeSelection: function () {
+                    var self = this;
+                    var ids = self.selection.map(function (row) {
+                        return row[self.primaryKeyField];
+                    });
+                    self.removeRows(ids);
+                },
+                removeRows: function (ids) {
+                    var self = this;
+                    if (!ids || ids.length === 0) {
+                        self.$message.warning('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁');
+                        return;
+                    }
+                    self.$confirm('纭畾鍒犻櫎閫変腑鐨勮褰曞悧锛�', '鎻愮ず', { type: 'warning' }).then(function () {
+                        $.ajax({
+                            url: baseUrl + '/' + simpleEntityName + '/delete/auth',
+                            method: 'POST',
+                            headers: self.authHeaders(),
+                            traditional: true,
+                            data: { 'ids[]': ids },
+                            success: function (res) {
+                                if (self.handleForbidden(res)) {
+                                    return;
+                                }
+                                if (!res || res.code !== 200) {
+                                    self.$message.error((res && res.msg) ? res.msg : '鍒犻櫎澶辫触');
+                                    return;
+                                }
+                                self.$message.success(res.msg || '鍒犻櫎鎴愬姛');
+                                self.selection = [];
+                                if (self.tableData.length === ids.length && self.page.curr > 1) {
+                                    self.page.curr = self.page.curr - 1;
+                                }
+                                self.loadTable();
+                            },
+                            error: function () {
+                                self.$message.error('鍒犻櫎澶辫触');
+                            }
+                        });
+                    }).catch(function () {});
+                },
+                exportRows: function () {
+                    var self = this;
+                    self.exporting = true;
+                    var requestBody = {
+                        fields: self.exportColumns.map(function (item) {
+                            return item.field;
+                        })
+                    };
+                    requestBody[simpleEntityName] = self.buildQueryParams();
+                    $.ajax({
+                        url: baseUrl + '/' + simpleEntityName + '/export/auth',
+                        method: 'POST',
+                        headers: $.extend({ 'Content-Type': 'application/json;charset=UTF-8' }, self.authHeaders()),
+                        data: JSON.stringify(requestBody),
+                        success: function (res) {
+                            self.exporting = false;
+                            if (self.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!res || res.code !== 200) {
+                                self.$message.error((res && res.msg) ? res.msg : '瀵煎嚭澶辫触');
+                                return;
+                            }
+                            createDownloadFile(
+                                simpleEntityName + '_' + buildTimestamp() + '.xls',
+                                self.exportColumns.map(function (item) {
+                                    return item.label;
+                                }),
+                                Array.isArray(res.data) ? res.data : []
+                            );
+                            self.$message.success('瀵煎嚭鎴愬姛');
+                        },
+                        error: function () {
+                            self.exporting = false;
+                            self.$message.error('瀵煎嚭澶辫触');
+                        }
+                    });
+                }
+            })
+        });
+    }
+
+})();
diff --git a/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js b/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js
index 1dd1f6c..dbab215 100644
--- a/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js
+++ b/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js
@@ -1345,20 +1345,20 @@
         },
         wrkSts: {
             label: '宸ヤ綔鐘舵��',
-            kind: 'foreign',
+            kind: 'enum',
             tableProp: 'wrkSts$',
             exportField: 'wrkSts$',
-            foreignQuery: 'basWrkStatus',
+            enumOptions: [],
             searchable: true,
             quickSearch: true,
             minWidth: 160
         },
         ioType: {
             label: '鍏ュ嚭搴撶被鍨�',
-            kind: 'foreign',
+            kind: 'enum',
             tableProp: 'ioType$',
             exportField: 'ioType$',
-            foreignQuery: 'basWrkIotype',
+            enumOptions: [],
             searchable: true,
             quickSearch: true,
             minWidth: 160
@@ -1385,10 +1385,18 @@
             minWidth: 90
         },
         sourceStaNo: {
-            label: '婧愮珯'
+            label: '婧愮珯',
+            kind: 'enum',
+            enumOptions: [],
+            searchable: true,
+            minWidth: 120
         },
         staNo: {
-            label: '鐩爣绔�'
+            label: '鐩爣绔�',
+            kind: 'enum',
+            enumOptions: [],
+            searchable: true,
+            minWidth: 120
         },
         sourceLocNo: {
             label: '婧愬簱浣�',
@@ -1416,6 +1424,7 @@
         },
         barcode: {
             label: '鏉$爜',
+            searchable: true,
             minWidth: 140
         },
         modiUser: {
@@ -1804,6 +1813,39 @@
         };
     }
 
+    function getFieldByName(fieldName) {
+        for (var i = 0; i < fieldMeta.length; i += 1) {
+            if (fieldMeta[i] && fieldMeta[i].field === fieldName) {
+                return fieldMeta[i];
+            }
+        }
+        return null;
+    }
+
+    function buildEnumOptions(records, valueField, labelField) {
+        var options = [];
+        var seen = {};
+        (records || []).forEach(function (item) {
+            if (!item) {
+                return;
+            }
+            var rawValue = item[valueField];
+            if (isEmptyValue(rawValue)) {
+                return;
+            }
+            var key = String(rawValue);
+            if (seen[key]) {
+                return;
+            }
+            seen[key] = true;
+            options.push({
+                rawValue: key,
+                label: isEmptyValue(item[labelField]) ? key : String(item[labelField])
+            });
+        });
+        return options;
+    }
+
     function handleForbidden(res) {
         if (res && res.code === 403) {
             top.location.href = baseUrl + '/';
@@ -1961,6 +2003,7 @@
                 }
             },
             created: function () {
+                this.loadSearchEnumOptions();
                 this.loadTable();
             },
             mounted: function () {
@@ -2038,6 +2081,63 @@
                     }
                     this.$set(this.searchForm, field.field, '');
                 },
+                loadSearchEnumOptions: function () {
+                    var self = this;
+                    [
+                        {
+                            field: 'wrkSts',
+                            url: baseUrl + '/basWrkStatus/list/auth',
+                            valueField: 'wrkSts',
+                            labelField: 'wrkDesc',
+                            limit: 200
+                        },
+                        {
+                            field: 'ioType',
+                            url: baseUrl + '/basWrkIotype/list/auth',
+                            valueField: 'ioType',
+                            labelField: 'ioDesc',
+                            limit: 200
+                        },
+                        {
+                            field: 'staNo',
+                            url: baseUrl + '/basStation/list/auth',
+                            valueField: 'stationId',
+                            labelField: 'stationId',
+                            limit: 500
+                        },
+                        {
+                            field: 'sourceStaNo',
+                            url: baseUrl + '/basStation/list/auth',
+                            valueField: 'stationId',
+                            labelField: 'stationId',
+                            limit: 500
+                        }
+                    ].forEach(function (config) {
+                        $.ajax({
+                            url: config.url,
+                            method: 'GET',
+                            headers: self.authHeaders(),
+                            data: {
+                                curr: 1,
+                                limit: config.limit
+                            },
+                            success: function (res) {
+                                if (self.handleForbidden(res)) {
+                                    return;
+                                }
+                                if (!res || res.code !== 200) {
+                                    return;
+                                }
+                                var payload = res.data || {};
+                                var field = getFieldByName(config.field);
+                                if (!field) {
+                                    return;
+                                }
+                                self.$set(field, 'enumOptions', buildEnumOptions(payload.records, config.valueField, config.labelField));
+                            }
+                        });
+                    });
+                },
                 buildQueryParams: function () {
                     var self = this;
                     var params = {
diff --git a/src/main/webapp/views/basStationErrLog/basStationErrLog.html b/src/main/webapp/views/basStationErrLog/basStationErrLog.html
new file mode 100644
index 0000000..6467147
--- /dev/null
+++ b/src/main/webapp/views/basStationErrLog/basStationErrLog.html
@@ -0,0 +1,670 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="utf-8">
+    <title>BasStationErrLog 绠$悊</title>
+    <meta name="renderer" content="webkit">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+    <link rel="stylesheet" href="../../static/vue/element/element.css">
+    <link rel="stylesheet" href="../../static/css/cool.css">
+    <style>
+        :root {
+            --card-bg: rgba(255, 255, 255, 0.94);
+            --card-border: rgba(216, 226, 238, 0.95);
+            --text-main: #243447;
+        }
+
+        [v-cloak] {
+            display: none;
+        }
+
+        html,
+        body {
+            margin: 0;
+            min-height: 100%;
+            color: var(--text-main);
+            font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
+            background:
+                radial-gradient(1000px 420px at 0% -10%, rgba(44, 107, 193, 0.12), transparent 56%),
+                radial-gradient(900px 400px at 100% 0%, rgba(28, 150, 126, 0.10), transparent 58%),
+                linear-gradient(180deg, #f2f6fb 0%, #f8fafc 100%);
+        }
+
+        .page-shell {
+            max-width: 1700px;
+            margin: 0 auto;
+            padding: 14px;
+            box-sizing: border-box;
+        }
+
+        .card-shell {
+            position: relative;
+            border-radius: 24px;
+            border: 1px solid var(--card-border);
+            background:
+                radial-gradient(760px 220px at -8% 0%, rgba(43, 117, 196, 0.05), transparent 55%),
+                radial-gradient(680px 200px at 108% 10%, rgba(24, 150, 129, 0.05), transparent 58%),
+                var(--card-bg);
+            box-shadow: 0 16px 32px rgba(44, 67, 96, 0.08);
+            overflow: hidden;
+        }
+
+        .card-body {
+            position: relative;
+            z-index: 1;
+        }
+
+        .list-toolbar {
+            padding: 12px 16px 10px;
+            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
+        }
+
+        .toolbar-main {
+            display: flex;
+            align-items: flex-start;
+            justify-content: space-between;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .toolbar-left {
+            flex: 1 1 960px;
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .toolbar-search {
+            flex: 1 1 auto;
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .toolbar-search-item {
+            flex: 0 0 152px;
+            min-width: 152px;
+        }
+
+        .toolbar-search-item.keyword {
+            flex: 0 0 220px;
+            min-width: 220px;
+        }
+
+        .toolbar-query-actions,
+        .toolbar-ops {
+            display: flex;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .toolbar-ops {
+            justify-content: flex-end;
+        }
+
+        .list-toolbar .el-input__inner,
+        .list-toolbar .el-range-editor.el-input__inner,
+        .advanced-panel .el-input__inner,
+        .advanced-panel .el-range-editor.el-input__inner {
+            height: 32px;
+            line-height: 32px;
+        }
+
+        .list-toolbar .el-range-editor.el-input__inner,
+        .advanced-panel .el-range-editor.el-input__inner {
+            align-items: center;
+        }
+
+        .list-toolbar .el-input__icon,
+        .advanced-panel .el-input__icon {
+            line-height: 32px;
+        }
+
+        .list-toolbar .el-range-editor .el-range__icon,
+        .list-toolbar .el-range-editor .el-range-separator,
+        .list-toolbar .el-range-editor .el-range__close-icon,
+        .advanced-panel .el-range-editor .el-range__icon,
+        .advanced-panel .el-range-editor .el-range-separator,
+        .advanced-panel .el-range-editor .el-range__close-icon {
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+            height: 100%;
+            line-height: 1;
+        }
+
+        .list-toolbar .el-button,
+        .advanced-panel .el-button {
+            padding: 8px 12px;
+            border-radius: 8px;
+        }
+
+        .advanced-panel {
+            padding: 10px 16px 12px;
+            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
+            background: rgba(248, 251, 254, 0.78);
+        }
+
+        .advanced-grid {
+            display: grid;
+            grid-template-columns: repeat(6, minmax(0, 1fr));
+            gap: 8px;
+        }
+
+        .advanced-item {
+            min-width: 0;
+        }
+
+        .advanced-item.span-2 {
+            grid-column: span 2;
+        }
+
+        .table-wrap {
+            padding: 10px 16px;
+        }
+
+        .table-shell {
+            border-radius: 20px;
+            overflow: hidden;
+            border: 1px solid rgba(217, 227, 238, 0.98);
+            background: rgba(255, 255, 255, 0.95);
+        }
+
+        .table-shell .el-table {
+            border-radius: 20px;
+            overflow: hidden;
+        }
+
+        .table-shell .el-table th {
+            background: #f7fafc;
+            color: #53677d;
+            font-weight: 700;
+        }
+
+        .payload-cell {
+            display: inline-block;
+            max-width: 280px;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+        }
+
+        .mono {
+            font-family: Menlo, Monaco, Consolas, "Liberation Mono", monospace;
+        }
+
+        .pager-bar {
+            padding: 0 16px 16px;
+            display: flex;
+            align-items: center;
+            justify-content: flex-end;
+        }
+
+        .column-popover {
+            max-width: 320px;
+        }
+
+        .column-popover-head {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            gap: 12px;
+            margin-bottom: 10px;
+            font-size: 13px;
+            font-weight: 700;
+            color: var(--text-main);
+        }
+
+        .column-list {
+            display: grid;
+            grid-template-columns: repeat(2, minmax(0, 1fr));
+            gap: 8px 10px;
+            max-height: 280px;
+            overflow: auto;
+            padding-right: 4px;
+        }
+
+        .dialog-panel .el-dialog {
+            border-radius: 24px;
+            overflow: hidden;
+        }
+
+        .dialog-panel .el-dialog__header {
+            padding: 22px 24px 12px;
+            background: linear-gradient(180deg, #f8fbff 0%, #f3f7fb 100%);
+            border-bottom: 1px solid rgba(224, 232, 241, 0.92);
+        }
+
+        .dialog-panel .el-dialog__title {
+            font-weight: 700;
+            color: var(--text-main);
+        }
+
+        .dialog-panel .el-dialog__body {
+            padding: 18px 24px 8px;
+        }
+
+        .dialog-footer {
+            display: flex;
+            justify-content: flex-end;
+            gap: 10px;
+        }
+
+        @media (max-width: 1520px) {
+            .advanced-grid {
+                grid-template-columns: repeat(5, minmax(0, 1fr));
+            }
+        }
+
+        @media (max-width: 1280px) {
+            .advanced-grid {
+                grid-template-columns: repeat(4, minmax(0, 1fr));
+            }
+        }
+
+        @media (max-width: 960px) {
+            .toolbar-left {
+                flex-basis: 100%;
+            }
+
+            .advanced-grid {
+                grid-template-columns: repeat(3, minmax(0, 1fr));
+            }
+
+            .advanced-item.span-2 {
+                grid-column: span 2;
+            }
+        }
+
+        @media (max-width: 720px) {
+            .page-shell {
+                padding: 12px;
+            }
+
+            .toolbar-search-item,
+            .toolbar-search-item.keyword {
+                min-width: 100%;
+                flex-basis: 100%;
+            }
+
+            .toolbar-query-actions,
+            .toolbar-ops {
+                width: 100%;
+            }
+
+            .advanced-grid {
+                grid-template-columns: repeat(2, minmax(0, 1fr));
+            }
+
+            .advanced-item.span-2 {
+                grid-column: span 2;
+            }
+        }
+
+        @media (max-width: 560px) {
+            .advanced-grid {
+                grid-template-columns: 1fr;
+            }
+
+            .advanced-item.span-2 {
+                grid-column: auto;
+            }
+
+            .list-toolbar,
+            .advanced-panel,
+            .table-wrap,
+            .pager-bar {
+                padding-left: 14px;
+                padding-right: 14px;
+            }
+
+            .column-list {
+                grid-template-columns: 1fr;
+            }
+        }
+    </style>
+</head>
+<body>
+<div id="app" class="page-shell" v-cloak>
+    <section class="card-shell list-card">
+        <div class="card-body">
+            <div class="list-toolbar">
+                <div class="toolbar-main">
+                    <div class="toolbar-left">
+                        <div class="toolbar-search">
+                            <div class="toolbar-search-item keyword">
+                                <el-input
+                                    v-model.trim="searchForm.condition"
+                                    size="small"
+                                    clearable
+                                    placeholder="璇疯緭鍏�"
+                                    @keyup.enter.native="handleSearch">
+                                </el-input>
+                            </div>
+                            <div
+                                v-for="field in quickSearchableFields"
+                                :key="'quick-' + field.field"
+                                class="toolbar-search-item">
+                                <el-select
+                                    v-if="field.kind === 'enum'"
+                                    v-model="searchForm[field.field]"
+                                    size="small"
+                                    clearable
+                                    :placeholder="field.label"
+                                    style="width: 100%;">
+                                    <el-option
+                                        v-for="option in field.enumOptions"
+                                        :key="'quick-' + field.field + '-' + option.rawValue"
+                                        :label="option.label"
+                                        :value="normalizeOptionValue(field, option.rawValue)">
+                                    </el-option>
+                                </el-select>
+                                <el-autocomplete
+                                    v-else-if="field.kind === 'foreign'"
+                                    v-model="searchDisplay[field.field]"
+                                    size="small"
+                                    :fetch-suggestions="getSuggestionFetcher(field)"
+                                    :placeholder="field.label"
+                                    style="width: 100%;"
+                                    @select="handleSearchForeignSelect(field, $event)"
+                                    @input="handleSearchForeignInput(field)">
+                                    <template slot-scope="{ item }">
+                                        <div class="mono">{{ item.value }}</div>
+                                        <div style="font-size:12px;color:#8a98ac;">ID: {{ item.id }}</div>
+                                    </template>
+                                </el-autocomplete>
+                                <el-select
+                                    v-else-if="field.kind === 'checkbox'"
+                                    v-model="searchForm[field.field]"
+                                    size="small"
+                                    clearable
+                                    :placeholder="field.label"
+                                    style="width: 100%;">
+                                    <el-option label="鏄�" :value="normalizeOptionValue(field, field.checkboxActiveRaw)"></el-option>
+                                    <el-option label="鍚�" :value="normalizeOptionValue(field, field.checkboxInactiveRaw)"></el-option>
+                                </el-select>
+                                <el-input
+                                    v-else
+                                    v-model.trim="searchForm[field.field]"
+                                    size="small"
+                                    clearable
+                                    :placeholder="field.label"
+                                    @keyup.enter.native="handleSearch">
+                                </el-input>
+                            </div>
+                        </div>
+                        <div class="toolbar-query-actions">
+                            <el-button size="small" type="primary" icon="el-icon-search" @click="handleSearch">鎼滅储</el-button>
+                            <el-button size="small" icon="el-icon-refresh-left" @click="handleReset">閲嶇疆</el-button>
+                            <el-button
+                                v-if="hasAdvancedFilters"
+                                size="small"
+                                plain
+                                :icon="advancedFiltersVisible ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
+                                @click="toggleAdvancedFilters">
+                                {{ advancedFiltersVisible ? '鏀惰捣' : '绛涢��' }}
+                            </el-button>
+                        </div>
+                    </div>
+                    <div class="toolbar-ops">
+                        <el-button size="small" type="primary" plain icon="el-icon-plus" @click="openCreateDialog">鏂板</el-button>
+                        <el-button size="small" type="danger" plain icon="el-icon-delete" :disabled="selection.length === 0" @click="removeSelection">鍒犻櫎</el-button>
+                        <el-popover
+                            placement="bottom"
+                            width="320"
+                            trigger="click"
+                            popper-class="column-popover">
+                            <div class="column-popover-head">
+                                <span>鍒楄缃�</span>
+                                <div>
+                                    <el-button type="text" @click="selectAllColumns">鍏ㄩ��</el-button>
+                                    <el-button type="text" @click="resetColumns">閲嶇疆</el-button>
+                                </div>
+                            </div>
+                            <div class="column-list">
+                                <el-checkbox
+                                    v-for="field in allColumns"
+                                    :key="'column-' + field.field"
+                                    :value="isColumnVisible(field.field)"
+                                    @change="toggleColumn(field.field, $event)">
+                                    {{ field.label }}
+                                </el-checkbox>
+                            </div>
+                            <el-button slot="reference" size="small" plain icon="el-icon-setting">鍒楄缃�</el-button>
+                        </el-popover>
+                        <el-button size="small" plain icon="el-icon-download" :loading="exporting" @click="exportRows">瀵煎嚭</el-button>
+                    </div>
+                </div>
+            </div>
+
+            <el-collapse-transition>
+                <div v-show="advancedFiltersVisible && hasAdvancedFilters" class="advanced-panel">
+                    <div class="advanced-grid">
+                        <div
+                            v-for="field in advancedSearchableFields"
+                            :key="'advanced-' + field.field"
+                            :class="['advanced-item', field.kind === 'date' ? 'span-2' : '']">
+                            <el-date-picker
+                                v-if="field.kind === 'date'"
+                                v-model="searchForm[field.field]"
+                                size="small"
+                                type="datetimerange"
+                                unlink-panels
+                                range-separator="鑷�"
+                                :start-placeholder="field.label + '寮�濮�'"
+                                :end-placeholder="field.label + '缁撴潫'"
+                                value-format="yyyy-MM-dd HH:mm:ss"
+                                style="width: 100%;">
+                            </el-date-picker>
+                            <el-select
+                                v-else-if="field.kind === 'enum'"
+                                v-model="searchForm[field.field]"
+                                size="small"
+                                clearable
+                                :placeholder="field.label"
+                                style="width: 100%;">
+                                <el-option
+                                    v-for="option in field.enumOptions"
+                                    :key="'advanced-' + field.field + '-' + option.rawValue"
+                                    :label="option.label"
+                                    :value="normalizeOptionValue(field, option.rawValue)">
+                                </el-option>
+                            </el-select>
+                            <el-autocomplete
+                                v-else-if="field.kind === 'foreign'"
+                                v-model="searchDisplay[field.field]"
+                                size="small"
+                                :fetch-suggestions="getSuggestionFetcher(field)"
+                                :placeholder="field.label"
+                                style="width: 100%;"
+                                @select="handleSearchForeignSelect(field, $event)"
+                                @input="handleSearchForeignInput(field)">
+                                <template slot-scope="{ item }">
+                                    <div class="mono">{{ item.value }}</div>
+                                    <div style="font-size:12px;color:#8a98ac;">ID: {{ item.id }}</div>
+                                </template>
+                            </el-autocomplete>
+                            <el-select
+                                v-else-if="field.kind === 'checkbox'"
+                                v-model="searchForm[field.field]"
+                                size="small"
+                                clearable
+                                :placeholder="field.label"
+                                style="width: 100%;">
+                                <el-option label="鏄�" :value="normalizeOptionValue(field, field.checkboxActiveRaw)"></el-option>
+                                <el-option label="鍚�" :value="normalizeOptionValue(field, field.checkboxInactiveRaw)"></el-option>
+                            </el-select>
+                            <el-input
+                                v-else
+                                v-model.trim="searchForm[field.field]"
+                                size="small"
+                                clearable
+                                :placeholder="field.label"
+                                @keyup.enter.native="handleSearch">
+                            </el-input>
+                        </div>
+                    </div>
+                </div>
+            </el-collapse-transition>
+
+            <div class="table-wrap">
+                <div class="table-shell">
+                    <el-table
+                        ref="dataTable"
+                        :key="'table-' + visibleColumnKeys.join('|')"
+                        v-loading="loading"
+                        :data="tableData"
+                        border
+                        stripe
+                        :height="tableHeight"
+                        @selection-change="handleSelectionChange"
+                        @sort-change="handleSortChange">
+                        <el-table-column type="selection" width="52" align="center"></el-table-column>
+                        <el-table-column
+                            v-for="field in visibleColumns"
+                            :key="field.field"
+                            :prop="field.field"
+                            :label="field.label"
+                            :width="field.primaryKey ? 90 : null"
+                            :min-width="field.primaryKey ? null : field.minWidth"
+                            :sortable="isSortableField(field) ? 'custom' : false"
+                            :show-overflow-tooltip="field.kind !== 'image'"
+                            align="center">
+                            <template slot-scope="scope">
+                                <el-image
+                                    v-if="field.kind === 'image' && getTableValue(scope.row, field)"
+                                    :src="getTableValue(scope.row, field)"
+                                    fit="cover"
+                                    style="width: 48px; height: 48px; border-radius: 10px;">
+                                </el-image>
+                                <el-tag v-else-if="field.kind === 'enum'" size="mini" type="success">
+                                    {{ valueOrDash(getTableValue(scope.row, field)) }}
+                                </el-tag>
+                                <el-tag v-else-if="field.kind === 'checkbox'" size="mini" :type="isCheckboxChecked(scope.row, field) ? 'success' : 'info'">
+                                    {{ isCheckboxChecked(scope.row, field) ? '鏄�' : '鍚�' }}
+                                </el-tag>
+                                <span v-else-if="field.textarea" class="payload-cell mono" :title="stringValue(getTableValue(scope.row, field))">
+                                    {{ valueOrDash(getTableValue(scope.row, field)) }}
+                                </span>
+                                <span v-else>{{ valueOrDash(getTableValue(scope.row, field)) }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column label="鎿嶄綔" width="160" fixed="right" align="center">
+                            <template slot-scope="scope">
+                                <el-button type="text" @click="openEditDialog(scope.row)">淇敼</el-button>
+                                <el-button type="text" style="color:#f56c6c;" @click="removeRows([scope.row[primaryKeyField]])">鍒犻櫎</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </div>
+            </div>
+
+            <div class="pager-bar">
+                <el-pagination
+                    small
+                    background
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :current-page="page.curr"
+                    :page-size="page.limit"
+                    :page-sizes="[15, 30, 50, 100, 200, 500]"
+                    :total="page.total"
+                    @current-change="handleCurrentChange"
+                    @size-change="handleSizeChange">
+                </el-pagination>
+            </div>
+        </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板杈撻�佺珯鐐瑰紓甯告棩蹇�' : '淇敼杈撻�佺珯鐐瑰紓甯告棩蹇�'"
+        :visible.sync="dialog.visible"
+        width="760px"
+        :close-on-click-modal="false">
+        <el-form
+            ref="dialogForm"
+            :model="dialogForm"
+            :rules="dialogRules"
+            label-width="110px"
+            size="small">
+            <el-row :gutter="16">
+                <el-col
+                    v-for="field in editableFields"
+                    :key="'dialog-' + field.field"
+                    :span="field.textarea || field.kind === 'image' ? 24 : 12">
+                    <el-form-item :label="field.label" :prop="field.field">
+                        <el-date-picker
+                            v-if="field.kind === 'date'"
+                            v-model="dialogForm[field.field]"
+                            type="datetime"
+                            value-format="yyyy-MM-dd HH:mm:ss"
+                            :placeholder="'璇烽�夋嫨' + field.label"
+                            style="width: 100%;">
+                        </el-date-picker>
+                        <el-select
+                            v-else-if="field.kind === 'enum'"
+                            v-model="dialogForm[field.field]"
+                            clearable
+                            :placeholder="'璇烽�夋嫨' + field.label"
+                            style="width: 100%;">
+                            <el-option
+                                v-for="option in field.enumOptions"
+                                :key="'dialog-' + field.field + '-' + option.rawValue"
+                                :label="option.label"
+                                :value="normalizeOptionValue(field, option.rawValue)">
+                            </el-option>
+                        </el-select>
+                        <el-autocomplete
+                            v-else-if="field.kind === 'foreign'"
+                            v-model="dialogDisplay[field.field]"
+                            :fetch-suggestions="getSuggestionFetcher(field)"
+                            :placeholder="'璇疯緭鍏�' + field.label"
+                            style="width: 100%;"
+                            @select="handleForeignSelect(field, $event)"
+                            @input="handleForeignInput(field)">
+                            <template slot-scope="{ item }">
+                                <div class="mono">{{ item.value }}</div>
+                                <div style="font-size:12px;color:#8a98ac;">ID: {{ item.id }}</div>
+                            </template>
+                        </el-autocomplete>
+                        <el-switch
+                            v-else-if="field.kind === 'checkbox'"
+                            v-model="dialogForm[field.field]"
+                            :active-value="normalizeOptionValue(field, field.checkboxActiveRaw)"
+                            :inactive-value="normalizeOptionValue(field, field.checkboxInactiveRaw)"
+                            active-color="#13ce66"
+                            inactive-color="#c0c4cc">
+                        </el-switch>
+                        <el-input
+                            v-else-if="field.textarea"
+                            v-model.trim="dialogForm[field.field]"
+                            type="textarea"
+                            :rows="3"
+                            :placeholder="'璇疯緭鍏�' + field.label">
+                        </el-input>
+                        <el-input
+                            v-else
+                            v-model.trim="dialogForm[field.field]"
+                            :placeholder="'璇疯緭鍏�' + field.label">
+                        </el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button @click="dialog.visible = false">鍙栨秷</el-button>
+            <el-button type="primary" :loading="dialog.submitting" @click="submitDialog">淇濆瓨</el-button>
+        </div>
+    </el-dialog>
+</div>
+
+<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
+<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
+<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
+<script type="text/javascript" src="../../static/vue/element/element.js"></script>
+<script type="text/javascript" src="../../static/js/basStationErrLog/basStationErrLog.js?v=20260331" charset="utf-8"></script>
+</body>
+</html>

--
Gitblit v1.9.1