From a637cdb04ab7e0954196ad0d8e7cc24ae22ee93a Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 10 三月 2026 13:22:22 +0800
Subject: [PATCH] #

---
 src/main/webapp/views/basRgvErrLog/basRgvErrLog.html             |  866 
 src/main/webapp/views/userLogin/userLogin.html                   |  436 
 src/main/webapp/views/basCrnpErrLog/basCrnpErrLog.html           |  866 
 src/main/webapp/static/js/basDevp/basDevp.js                     | 2829 ++
 src/main/webapp/views/apiLog/apiLog.html                         |  819 
 src/main/webapp/static/js/basDualCrnpErrLog/basDualCrnpErrLog.js | 1420 
 src/main/webapp/static/js/operateLog/operateLog.js               | 1648 +
 src/main/java/com/zy/system/entity/User.java                     |    2 
 src/main/webapp/static/js/httpRequestLog/httpRequestLog.js       | 1372 
 src/main/webapp/static/js/basDualCrnpOpt/basDualCrnpOpt.js       | 1277 
 src/main/webapp/static/js/basLocSts/basLocSts.js                 | 1487 
 src/main/webapp/views/basCrnpOpt/basCrnpOpt.html                 |  802 
 src/main/webapp/static/js/login/login.js                         |  270 
 src/main/webapp/views/basRgvOpt/basRgvOpt.html                   |  802 
 src/main/webapp/static/js/basCrnpOpt/basCrnpOpt.js               | 1277 
 src/main/webapp/views/basCrnpErr/basCrnpErr.html                 |  730 
 src/main/java/com/zy/system/entity/UserLogin.java                |    2 
 src/main/webapp/views/resource/resource.html                     |  583 
 src/main/java/com/zy/common/web/AuthController.java              |   17 
 src/main/resources/templates/Js.txt                              |   41 
 src/main/webapp/views/basDevp/basDevp.html                       |  834 
 src/main/webapp/static/js/basRgvErrLog/basRgvErrLog.js           | 1420 
 src/main/webapp/views/basLocSts/basLocSts.html                   |  692 
 src/main/java/com/zy/system/entity/Api.java                      |    3 
 src/main/webapp/static/js/basDualCrnp/basDualCrnp.js             | 1353 
 src/main/webapp/static/js/deviceConfig/deviceConfig.js           | 1375 
 src/main/webapp/views/basRgvErr/basRgvErr.html                   |  766 
 src/main/webapp/views/basCrnp/basCrnp.html                       |  788 
 src/main/webapp/views/httpRequestLog/httpRequestLog.html         |  751 
 src/main/webapp/views/watch/console.html                         |   13 
 src/main/webapp/views/wrkMast/wrkMast.html                       |   10 
 src/main/webapp/static/js/basCrnpErr/basCrnpErr.js               | 1144 
 src/main/webapp/views/basWrkIotype/basWrkIotype.html             |  692 
 src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java  |    7 
 src/main/webapp/static/js/basCrnpErrLog/basCrnpErrLog.js         | 1420 
 src/main/webapp/static/js/basRgvErr/basRgvErr.js                 | 1144 
 src/main/java/com/core/generators/CoolGenerator.java             |  133 
 src/main/webapp/views/basDualCrnp/basDualCrnp.html               |  824 
 src/main/webapp/views/detail.html                                |  406 
 src/main/webapp/views/wrkMastLog/wrkMastLog.html                 |  730 
 src/main/webapp/static/js/detail/detail.js                       |  247 
 src/main/webapp/views/user/user.html                             |  799 
 src/main/resources/templates/Html.txt                            |    3 
 src/main/webapp/views/operateLog/operateLog.html                 |  706 
 src/main/webapp/views/role/role.html                             |  375 
 src/main/webapp/static/js/basRgvOpt/basRgvOpt.js                 | 1277 
 src/main/java/com/zy/system/entity/Host.java                     |    3 
 src/main/webapp/views/basDualCrnpOpt/basDualCrnpOpt.html         |  802 
 src/main/webapp/views/login.html                                 |  881 
 src/main/webapp/views/config/config.html                         |  707 
 src/main/webapp/static/js/basStationOpt/basStationOpt.js         | 1277 
 src/main/webapp/views/basDualCrnpErrLog/basDualCrnpErrLog.html   |  866 
 src/main/webapp/views/basWrkStatus/basWrkStatus.html             |  692 
 src/main/webapp/views/basRgv/basRgv.html                         |  770 
 src/main/webapp/views/deviceConfig/deviceConfig.html             |  770 
 src/main/webapp/static/js/permission/permission.js               |  842 
 src/main/webapp/static/js/resource/resource.js                   |  825 
 src/main/webapp/views/basStationOpt/basStationOpt.html           |  802 
 src/main/webapp/static/js/basDualCrnpErr/basDualCrnpErr.js       | 1144 
 src/main/webapp/views/basMap/basMap.html                         |  759 
 src/main/webapp/static/js/password/password.js                   |  169 
 src/main/webapp/static/js/wrkMastLog/wrkMastLog.js               | 2564 +
 src/main/webapp/static/js/config/config.js                       | 1923 +
 src/main/webapp/static/js/user/user.js                           | 3573 ++
 src/main/webapp/static/js/wrkLastno/wrkLastno.js                 | 1724 +
 src/main/webapp/views/wrkLastno/wrkLastno.html                   |  698 
 src/main/webapp/views/locMast/locMast.html                       |  806 
 src/main/webapp/static/js/basStation/basStation.js               | 1492 
 src/main/webapp/static/js/apiLog/apiLog.js                       | 2105 +
 src/main/java/com/zy/system/entity/OperateLog.java               |    2 
 src/main/webapp/static/js/role/role.js                           |  950 
 src/main/webapp/views/permission/permission.html                 |  458 
 src/main/webapp/static/js/basMap/basMap.js                       | 1490 
 src/main/webapp/views/basStation/basStation.html                 |  789 
 src/main/webapp/static/js/basCrnp/basCrnp.js                     | 1315 
 /dev/null                                                        |  196 
 src/main/webapp/views/password.html                              |  203 
 src/main/webapp/static/js/basWrkIotype/basWrkIotype.js           | 1544 
 src/main/webapp/static/js/userLogin/userLogin.js                 |  821 
 src/main/webapp/static/js/basRgv/basRgv.js                       | 1163 
 src/main/webapp/static/js/locMast/locMast.js                     | 2305 +
 src/main/webapp/static/js/basWrkStatus/basWrkStatus.js           | 1488 
 src/main/webapp/views/basDualCrnpErr/basDualCrnpErr.html         |  766 
 83 files changed, 60,285 insertions(+), 14,857 deletions(-)

diff --git a/src/main/java/com/core/generators/CoolGenerator.java b/src/main/java/com/core/generators/CoolGenerator.java
index 250c174..7c0aa7f 100644
--- a/src/main/java/com/core/generators/CoolGenerator.java
+++ b/src/main/java/com/core/generators/CoolGenerator.java
@@ -26,6 +26,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class CoolGenerator {
 
@@ -69,6 +71,7 @@
     private String primaryKeyType;
     private String majorColumn;
     private String systemPackage;
+    private Map<String, String> existingFieldLabels = new LinkedHashMap<>();
 
     public void build() throws Exception {
         init();
@@ -173,6 +176,7 @@
         primaryKeyColumn = resolvePrimaryKeyColumn();
         primaryKeyType = resolvePrimaryKeyType();
         majorColumn = resolveMajorColumn();
+        existingFieldLabels = loadExistingFieldLabels();
         entityImport = buildEntityImports();
         entityContent = buildEntityContent();
         xmlContent = buildXmlContent();
@@ -236,7 +240,7 @@
     private static List<Column> getColumns(Connection connection, String table, boolean withForeignKey, SqlOsType sqlOsType) throws Exception {
         List<Column> columns = new ArrayList<>();
         DatabaseMetaData metaData = connection.getMetaData();
-        TableRef tableRef = parseTableRef(table);
+        TableRef tableRef = resolveTableRef(connection, table, sqlOsType);
         Set<String> primaryKeys = new LinkedHashSet<>();
         ResultSet pkRs = metaData.getPrimaryKeys(tableRef.catalog, tableRef.schema, tableRef.table);
         try {
@@ -247,10 +251,14 @@
             pkRs.close();
         }
 
+        Map<String, Column> uniqueColumns = new LinkedHashMap<>();
         ResultSet columnRs = metaData.getColumns(tableRef.catalog, tableRef.schema, tableRef.table, null);
         try {
             while (columnRs.next()) {
                 String columnName = columnRs.getString("COLUMN_NAME");
+                if (uniqueColumns.containsKey(columnName)) {
+                    continue;
+                }
                 int sqlType = columnRs.getInt("DATA_TYPE");
                 String type = GeneratorUtils.getType(sqlType);
                 if (Cools.isEmpty(type)) {
@@ -261,12 +269,13 @@
                 boolean mainKey = primaryKey && isAutoIncrement(columnRs);
                 boolean notNull = columnRs.getInt("NULLABLE") == DatabaseMetaData.columnNoNulls;
                 Integer length = GeneratorUtils.getColumnLength(columnRs.getString("TYPE_NAME"));
-                columns.add(new Column(connection, columnName, type, remarks, primaryKey, mainKey, notNull, length,
+                uniqueColumns.put(columnName, new Column(connection, columnName, type, remarks, primaryKey, mainKey, notNull, length,
                         withForeignKey, sqlOsType));
             }
         } finally {
             columnRs.close();
         }
+        columns.addAll(uniqueColumns.values());
         for (Column column : columns) {
             System.out.println(column.toString());
         }
@@ -281,12 +290,26 @@
         }
     }
 
-    private static TableRef parseTableRef(String table) {
+    private static TableRef resolveTableRef(Connection connection, String table, SqlOsType sqlOsType) throws SQLException {
+        String catalog = connection.getCatalog();
+        String schema = null;
+        try {
+            schema = connection.getSchema();
+        } catch (SQLException ignored) {
+        }
         if (table != null && table.contains(".")) {
             String[] arr = table.split("\\.");
-            return new TableRef(null, arr[0], arr[arr.length - 1]);
+            String scope = arr[0];
+            String tableName = arr[arr.length - 1];
+            if (sqlOsType == SqlOsType.MYSQL) {
+                return new TableRef(scope, null, tableName);
+            }
+            return new TableRef(catalog, scope, tableName);
         }
-        return new TableRef(null, null, table);
+        if (sqlOsType == SqlOsType.MYSQL) {
+            return new TableRef(catalog, null, table);
+        }
+        return new TableRef(catalog, schema, table);
     }
 
     private String resolvePrimaryKeyColumn() {
@@ -606,7 +629,7 @@
             builder.append("    {\n");
             builder.append("        field: '").append(column.getHumpName()).append("',\n");
             builder.append("        columnName: '").append(column.getName()).append("',\n");
-            builder.append("        label: '").append(escapeJs(GeneratorUtils.supportHtmlName(column.getComment()))).append("',\n");
+            builder.append("        label: '").append(escapeJs(resolveVueFieldLabel(column))).append("',\n");
             builder.append("        tableProp: '").append(resolveDisplayField(column)).append("',\n");
             builder.append("        exportField: '").append(resolveDisplayField(column)).append("',\n");
             builder.append("        kind: '").append(resolveVueFieldKind(column)).append("',\n");
@@ -740,6 +763,104 @@
         return column.getHumpName();
     }
 
+    private String resolveVueFieldLabel(Column column) {
+        String label = GeneratorUtils.supportHtmlName(column.getComment());
+        if (!Cools.isEmpty(label)) {
+            return label;
+        }
+        label = existingFieldLabels.get(column.getHumpName());
+        if (!Cools.isEmpty(label)) {
+            return label;
+        }
+        return buildFallbackLabel(column);
+    }
+
+    private String buildFallbackLabel(Column column) {
+        String raw = Cools.isEmpty(column.getName()) ? column.getHumpName() : column.getName();
+        if (Cools.isEmpty(raw)) {
+            return "";
+        }
+        raw = raw.replace("$", "")
+                .replaceAll("([a-z0-9])([A-Z])", "$1 $2")
+                .replace("_", " ")
+                .replaceAll("\\s+", " ")
+                .trim();
+        if (Cools.isEmpty(raw)) {
+            return "";
+        }
+        StringBuilder builder = new StringBuilder();
+        for (String item : raw.split(" ")) {
+            if (item.isEmpty()) {
+                continue;
+            }
+            if (builder.length() > 0) {
+                builder.append(" ");
+            }
+            builder.append(item.substring(0, 1).toUpperCase()).append(item.substring(1));
+        }
+        return builder.toString();
+    }
+
+    private Map<String, String> loadExistingFieldLabels() {
+        Map<String, String> labelMap = new LinkedHashMap<>();
+        Path jsPath = Paths.get(frontendPrefixPath + "src/main/webapp/static/js/" + simpleEntityName + "/" + simpleEntityName + ".js");
+        Path htmlPath = Paths.get(frontendPrefixPath + "src/main/webapp/views/" + simpleEntityName + "/" + simpleEntityName + ".html");
+        Path detailPath = Paths.get(frontendPrefixPath + "src/main/webapp/views/" + simpleEntityName + "/" + simpleEntityName + "_detail.html");
+        try {
+            mergeExistingLabels(labelMap, jsPath, Pattern.compile("field:\\s*'([^']+)'.*?label:\\s*'([^']+)'", Pattern.DOTALL));
+            mergeExistingLabels(labelMap, jsPath, Pattern.compile("field:\\s*'([^']+)'[^\\n]*title:\\s*'([^']+)'"));
+            mergeExistingLabels(labelMap, detailPath, Pattern.compile("<label[^>]*>(.*?)</label>.*?<(?:(?:input)|(?:select))[^>]*\\sid=\"([^\"]+)\"", Pattern.DOTALL), true);
+            mergeExistingLabels(labelMap, htmlPath, Pattern.compile("name=\"([^\"]+)\"[^>]*placeholder=\"([^\"]+)\""));
+        } catch (IOException ignored) {
+        }
+        return labelMap;
+    }
+
+    private void mergeExistingLabels(Map<String, String> labelMap, Path path, Pattern pattern) throws IOException {
+        mergeExistingLabels(labelMap, path, pattern, false);
+    }
+
+    private void mergeExistingLabels(Map<String, String> labelMap, Path path, Pattern pattern, boolean reversed) throws IOException {
+        if (path == null || !Files.exists(path)) {
+            return;
+        }
+        String content = Files.readString(path, StandardCharsets.UTF_8);
+        Matcher matcher = pattern.matcher(content);
+        while (matcher.find()) {
+            String field = normalizeExistingField(reversed ? matcher.group(2) : matcher.group(1));
+            String label = cleanExistingLabel(reversed ? matcher.group(1) : matcher.group(2));
+            if (Cools.isEmpty(field, label) || labelMap.containsKey(field)) {
+                continue;
+            }
+            labelMap.put(field, label);
+        }
+    }
+
+    private String normalizeExistingField(String field) {
+        if (Cools.isEmpty(field)) {
+            return "";
+        }
+        field = field.trim();
+        if (field.endsWith("$")) {
+            field = field.substring(0, field.length() - 1);
+        }
+        if (field.contains("_")) {
+            return GeneratorUtils._convert(field, true);
+        }
+        return field;
+    }
+
+    private String cleanExistingLabel(String label) {
+        if (Cools.isEmpty(label)) {
+            return "";
+        }
+        label = label.replaceAll("<[^>]+>", "")
+                .replace("锛�", "")
+                .replace(":", "")
+                .trim();
+        return label;
+    }
+
     private List<Column> getNonPrimaryColumns() {
         List<Column> result = new ArrayList<>();
         for (Column column : columns) {
diff --git a/src/main/java/com/zy/common/web/AuthController.java b/src/main/java/com/zy/common/web/AuthController.java
index 80bff56..cf7f800 100644
--- a/src/main/java/com/zy/common/web/AuthController.java
+++ b/src/main/java/com/zy/common/web/AuthController.java
@@ -157,12 +157,14 @@
 
                     // 鏄惁鎷ユ湁鏌ョ湅鏉冮檺
                     if (getUserId() != 9527) {
-                        Resource view = resourceService.getOne(new QueryWrapper<Resource>().eq("resource_id", resource.getId()).like("code", "view"));
+                        Resource view = firstResource(new QueryWrapper<Resource>()
+                                .eq("resource_id", resource.getId())
+                                .like("code", "view"));
                         if (!Cools.isEmpty(view)){
                             RoleResource param = new RoleResource();
                             param.setResourceId(view.getId());
                             param.setRoleId(user.getRoleId());
-                            if (null == roleResourceService.getOne(new QueryWrapper<>(param))){
+                            if (!existsRoleResource(new QueryWrapper<>(param))){
                                 continue;
                             }
                         }
@@ -342,5 +344,16 @@
         return R.ok(resources);
     }
 
+    private Resource firstResource(QueryWrapper<Resource> wrapper) {
+        wrapper.last("limit 1");
+        List<Resource> list = resourceService.list(wrapper);
+        return list.isEmpty() ? null : list.get(0);
+    }
+
+    private boolean existsRoleResource(QueryWrapper<RoleResource> wrapper) {
+        wrapper.last("limit 1");
+        return !roleResourceService.list(wrapper).isEmpty();
+    }
+
 
 }
diff --git a/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java b/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java
index 93f7e4a..d697601 100644
--- a/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java
+++ b/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java
@@ -199,18 +199,19 @@
         WrkMast stationOneWrkMast = null;
         WrkMast stationTwoWrkMast = null;
 
-        List<Integer> disableList = basDualCrnp.getDisableStationOneBays$();
+        List<Integer> disableOneList = basDualCrnp.getDisableStationOneBays$();
+        List<Integer> disableTwoList = basDualCrnp.getDisableStationTwoBays$();
 
         for (WrkMast wrkMast : outTaskList) {
             if (stationOneWrkMast == null) {
-                if (!disableList.contains(Utils.getBay(wrkMast.getSourceLocNo()))) {
+                if (!disableOneList.contains(Utils.getBay(wrkMast.getSourceLocNo()))) {
                     stationOneWrkMast = wrkMast;
                     continue;
                 }
             }
 
             if (stationTwoWrkMast == null) {
-                if (!disableList.contains(Utils.getBay(wrkMast.getSourceLocNo()))) {
+                if (!disableTwoList.contains(Utils.getBay(wrkMast.getSourceLocNo()))) {
                     stationTwoWrkMast = wrkMast;
                     continue;
                 }
diff --git a/src/main/java/com/zy/system/entity/Api.java b/src/main/java/com/zy/system/entity/Api.java
index cad1c5b..faacde1 100644
--- a/src/main/java/com/zy/system/entity/Api.java
+++ b/src/main/java/com/zy/system/entity/Api.java
@@ -5,6 +5,7 @@
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.core.common.Cools;
+import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
 import java.text.SimpleDateFormat;
@@ -44,12 +45,14 @@
     /**
      * 娣诲姞鏃堕棿
      */
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
     @TableField("create_time")
     private Date createTime;
 
     /**
      * 淇敼鏃堕棿
      */
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
     @TableField("update_time")
     private Date updateTime;
 
diff --git a/src/main/java/com/zy/system/entity/Host.java b/src/main/java/com/zy/system/entity/Host.java
index 538e5e7..cbd59bf 100644
--- a/src/main/java/com/zy/system/entity/Host.java
+++ b/src/main/java/com/zy/system/entity/Host.java
@@ -5,6 +5,7 @@
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.core.common.Cools;
+import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
 import java.text.SimpleDateFormat;
@@ -34,12 +35,14 @@
     /**
      * 娣诲姞鏃堕棿
      */
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
     @TableField("create_time")
     private Date createTime;
 
     /**
      * 淇敼鏃堕棿
      */
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
     @TableField("update_time")
     private Date updateTime;
 
diff --git a/src/main/java/com/zy/system/entity/OperateLog.java b/src/main/java/com/zy/system/entity/OperateLog.java
index 726ef71..0450778 100644
--- a/src/main/java/com/zy/system/entity/OperateLog.java
+++ b/src/main/java/com/zy/system/entity/OperateLog.java
@@ -7,6 +7,7 @@
 import com.core.common.Cools;
 import com.core.common.SpringUtils;
 import com.zy.system.service.UserService;
+import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
 import java.text.SimpleDateFormat;
@@ -52,6 +53,7 @@
     /**
      * 娣诲姞鏃堕棿
      */
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
     @TableField("create_time")
     private Date createTime;
 
diff --git a/src/main/java/com/zy/system/entity/User.java b/src/main/java/com/zy/system/entity/User.java
index 4481956..519c0c6 100644
--- a/src/main/java/com/zy/system/entity/User.java
+++ b/src/main/java/com/zy/system/entity/User.java
@@ -8,6 +8,7 @@
 import com.core.common.SpringUtils;
 import com.zy.system.service.HostService;
 import com.zy.system.service.RoleService;
+import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
 import java.text.SimpleDateFormat;
@@ -56,6 +57,7 @@
     /**
      * 娉ㄥ唽鏃堕棿
      */
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
     @TableField("create_time")
     private Date createTime;
 
diff --git a/src/main/java/com/zy/system/entity/UserLogin.java b/src/main/java/com/zy/system/entity/UserLogin.java
index 2528b3f..d95e279 100644
--- a/src/main/java/com/zy/system/entity/UserLogin.java
+++ b/src/main/java/com/zy/system/entity/UserLogin.java
@@ -7,6 +7,7 @@
 import com.core.common.Cools;
 import com.core.common.SpringUtils;
 import com.zy.system.service.UserService;
+import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
 import java.text.SimpleDateFormat;
@@ -37,6 +38,7 @@
     /**
      * 娣诲姞鏃堕棿
      */
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
     @TableField("create_time")
     private Date createTime;
 
diff --git a/src/main/resources/templates/Html.txt b/src/main/resources/templates/Html.txt
index 4f8e5de..e57ca81 100644
--- a/src/main/resources/templates/Html.txt
+++ b/src/main/resources/templates/Html.txt
@@ -514,6 +514,7 @@
                 <div class="table-shell">
                     <el-table
                         ref="dataTable"
+                        :key="'table-' + visibleColumnKeys.join('|')"
                         v-loading="loading"
                         :data="tableData"
                         border
@@ -664,6 +665,6 @@
 <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/@{SIMPLEENTITYNAME}/@{SIMPLEENTITYNAME}.js" charset="utf-8"></script>
+<script type="text/javascript" src="../../static/js/@{SIMPLEENTITYNAME}/@{SIMPLEENTITYNAME}.js?v=20260310" charset="utf-8"></script>
 </body>
 </html>
diff --git a/src/main/resources/templates/Js.txt b/src/main/resources/templates/Js.txt
index ca4c8d2..e101d34 100644
--- a/src/main/resources/templates/Js.txt
+++ b/src/main/resources/templates/Js.txt
@@ -2,9 +2,43 @@
     var simpleEntityName = '@{SIMPLEENTITYNAME}';
     var entityName = '@{ENTITYNAME}';
     var primaryKeyField = '@{PRIMARYKEYCOLUMN0}';
-    var fieldMeta = [
+    var fieldMeta = dedupeFieldMeta([
 @{VUEFIELDMETA}
-    ];
+    ]);
+
+    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 === '';
@@ -165,6 +199,9 @@
                 }
                 return;
             }
+            if (field.kind === 'date' && isEmptyValue(value)) {
+                return;
+            }
             if (field.kind === 'foreign' && isEmptyValue(value)) {
                 value = null;
             }
diff --git a/src/main/webapp/static/js/apiLog/apiLog.js b/src/main/webapp/static/js/apiLog/apiLog.js
index 8e72f1a..d967227 100644
--- a/src/main/webapp/static/js/apiLog/apiLog.js
+++ b/src/main/webapp/static/js/apiLog/apiLog.js
@@ -1,268 +1,1867 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'apiLog';
+    var entityName = 'ApiLog';
+    var primaryKeyField = 'id';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'uuid',
+        columnName: 'uuid',
+        label: '鏃ュ織缂栧彿',
+        tableProp: 'uuid',
+        exportField: 'uuid',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'namespace',
+        columnName: 'namespace',
+        label: '鍚嶇О绌洪棿',
+        tableProp: 'namespace',
+        exportField: 'namespace',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'url',
+        columnName: 'url',
+        label: '鎺ュ彛鍦板潃',
+        tableProp: 'url',
+        exportField: 'url',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appkey',
+        columnName: 'appkey',
+        label: '骞冲彴瀵嗛挜',
+        tableProp: 'appkey',
+        exportField: 'appkey',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'timestamp',
+        columnName: 'timestamp',
+        label: '鏃堕棿鎴�',
+        tableProp: 'timestamp',
+        exportField: 'timestamp',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'clientIp',
+        columnName: 'client_ip',
+        label: '瀹㈡埛绔疘P',
+        tableProp: 'clientIp',
+        exportField: 'clientIp',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鍐呭',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鍐呭',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'err',
+        columnName: 'err',
+        label: '寮傚父鍐呭',
+        tableProp: 'err',
+        exportField: 'err',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'result',
+        columnName: 'result',
+        label: '缁撴灉',
+        tableProp: 'result',
+        exportField: 'result',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: '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: '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: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'uuid',
+        columnName: 'uuid',
+        label: '鏃ュ織缂栧彿',
+        tableProp: 'uuid',
+        exportField: 'uuid',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'namespace',
+        columnName: 'namespace',
+        label: '鍚嶇О绌洪棿',
+        tableProp: 'namespace',
+        exportField: 'namespace',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'url',
+        columnName: 'url',
+        label: '鎺ュ彛鍦板潃',
+        tableProp: 'url',
+        exportField: 'url',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appkey',
+        columnName: 'appkey',
+        label: '骞冲彴瀵嗛挜',
+        tableProp: 'appkey',
+        exportField: 'appkey',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'timestamp',
+        columnName: 'timestamp',
+        label: '鏃堕棿鎴�',
+        tableProp: 'timestamp',
+        exportField: 'timestamp',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'clientIp',
+        columnName: 'client_ip',
+        label: '瀹㈡埛绔疘P',
+        tableProp: 'clientIp',
+        exportField: 'clientIp',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鍐呭',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鍐呭',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'err',
+        columnName: 'err',
+        label: '寮傚父鍐呭',
+        tableProp: 'err',
+        exportField: 'err',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'result',
+        columnName: 'result',
+        label: '缁撴灉',
+        tableProp: 'result',
+        exportField: 'result',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: '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: '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: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'uuid',
+        columnName: 'uuid',
+        label: '鏃ュ織缂栧彿',
+        tableProp: 'uuid',
+        exportField: 'uuid',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'namespace',
+        columnName: 'namespace',
+        label: '鍚嶇О绌洪棿',
+        tableProp: 'namespace',
+        exportField: 'namespace',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'url',
+        columnName: 'url',
+        label: '鎺ュ彛鍦板潃',
+        tableProp: 'url',
+        exportField: 'url',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appkey',
+        columnName: 'appkey',
+        label: '骞冲彴瀵嗛挜',
+        tableProp: 'appkey',
+        exportField: 'appkey',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'timestamp',
+        columnName: 'timestamp',
+        label: '鏃堕棿鎴�',
+        tableProp: 'timestamp',
+        exportField: 'timestamp',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'clientIp',
+        columnName: 'client_ip',
+        label: '瀹㈡埛绔疘P',
+        tableProp: 'clientIp',
+        exportField: 'clientIp',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鍐呭',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鍐呭',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'err',
+        columnName: 'err',
+        label: '寮傚父鍐呭',
+        tableProp: 'err',
+        exportField: 'err',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'result',
+        columnName: 'result',
+        label: '缁撴灉',
+        tableProp: 'result',
+        exportField: 'result',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: '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: '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: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'uuid',
+        columnName: 'uuid',
+        label: '鏃ュ織缂栧彿',
+        tableProp: 'uuid',
+        exportField: 'uuid',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'namespace',
+        columnName: 'namespace',
+        label: '鍚嶇О绌洪棿',
+        tableProp: 'namespace',
+        exportField: 'namespace',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'url',
+        columnName: 'url',
+        label: '鎺ュ彛鍦板潃',
+        tableProp: 'url',
+        exportField: 'url',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appkey',
+        columnName: 'appkey',
+        label: '骞冲彴瀵嗛挜',
+        tableProp: 'appkey',
+        exportField: 'appkey',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'timestamp',
+        columnName: 'timestamp',
+        label: '鏃堕棿鎴�',
+        tableProp: 'timestamp',
+        exportField: 'timestamp',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'clientIp',
+        columnName: 'client_ip',
+        label: '瀹㈡埛绔疘P',
+        tableProp: 'clientIp',
+        exportField: 'clientIp',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鍐呭',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鍐呭',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'err',
+        columnName: 'err',
+        label: '寮傚父鍐呭',
+        tableProp: 'err',
+        exportField: 'err',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'result',
+        columnName: 'result',
+        label: '缁撴灉',
+        tableProp: 'result',
+        exportField: 'result',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: '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: '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'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#apiLog',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/apiLog/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            // ,{field: 'id', align: 'center',title: 'ID'}
-            // ,{field: 'uuid', align: 'center',title: '鏃ュ織缂栧彿'}
-            ,{field: 'namespace', align: 'center',title: '鍚嶇О绌洪棿'}
-            ,{field: 'url', align: 'center',title: '鎺ュ彛鍦板潃'}
-            // ,{field: 'appkey', align: 'center',title: '骞冲彴瀵嗛挜'}
-            // ,{field: 'timestamp', align: 'center',title: '鏃堕棿鎴�'}
-            // ,{field: 'clientIp', align: 'center',title: '瀹㈡埛绔疘P'}
-            ,{field: 'request', align: 'center',title: '璇锋眰鍐呭'}
-            ,{field: 'response', align: 'center',title: '鍝嶅簲鍐呭'}
-            // ,{field: 'err', align: 'center',title: '寮傚父鍐呭'}
-            ,{field: 'result$', align: 'center',title: '缁撴灉', templet: '#resTpl', width: 80}
-            // ,{field: 'status$', align: 'center',title: '鐘舵��'}
-            ,{field: 'createTime$', align: 'center',title: '娣诲姞鏃堕棿'}
-            // ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞', hide: true}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width: 80}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(apiLog)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(apiLog)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'apiLog': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/apiLog/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(apiLog)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/apiLog/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/apiLog/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basCrnp/basCrnp.js b/src/main/webapp/static/js/basCrnp/basCrnp.js
index d135083..ebaa1e6 100644
--- a/src/main/webapp/static/js/basCrnp/basCrnp.js
+++ b/src/main/webapp/static/js/basCrnp/basCrnp.js
@@ -1,270 +1,1075 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basCrnp';
+    var entityName = 'BasCrnp';
+    var primaryKeyField = 'crnNo';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'crnNo',
+        columnName: 'crn_no',
+        label: '缂栥��銆�鍙�',
+        tableProp: 'crnNo',
+        exportField: 'crnNo',
+        kind: 'text',
+        valueType: 'number',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        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: 'inEnable',
+        columnName: 'in_enable',
+        label: '鍙叆(checkBox)',
+        tableProp: 'inEnable',
+        exportField: 'inEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outEnable',
+        columnName: 'out_enable',
+        label: '鍙嚭(checkBox)',
+        tableProp: 'outEnable',
+        exportField: 'outEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'createBy',
+        columnName: 'create_by',
+        label: '鍒涘缓浜哄憳',
+        tableProp: 'createBy',
+        exportField: 'createBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'updateBy',
+        columnName: 'update_by',
+        label: '淇敼浜哄憳',
+        tableProp: 'updateBy',
+        exportField: 'updateBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: '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: 'controlRows',
+        columnName: 'control_rows',
+        label: '鎺у埗搴撲綅鎺掑彿',
+        tableProp: 'controlRows',
+        exportField: 'controlRows',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 134,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'deepRows',
+        columnName: 'deep_rows',
+        label: '娣卞簱浣嶆帓鍙�',
+        tableProp: 'deepRows',
+        exportField: 'deepRows',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inStationList',
+        columnName: 'in_station_list',
+        label: '鍏ュ簱绔欑偣',
+        tableProp: 'inStationList',
+        exportField: 'inStationList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outStationList',
+        columnName: 'out_station_list',
+        label: '鍑哄簱绔欑偣',
+        tableProp: 'outStationList',
+        exportField: 'outStationList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'maxInTask',
+        columnName: 'max_in_task',
+        label: '鏈�澶у叆搴撲换鍔℃暟',
+        tableProp: 'maxInTask',
+        exportField: 'maxInTask',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 152,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'maxOutTask',
+        columnName: 'max_out_task',
+        label: '鏈�澶у嚭搴撲换鍔℃暟',
+        tableProp: 'maxOutTask',
+        exportField: 'maxOutTask',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 152,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basCrnp',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basCrnp/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'crnNo', align: 'center',title: '缂栧彿'}
-            ,{field: 'status$', align: 'center',title: '鐘舵��'}
-            // ,{field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'inEnable', align: 'center',title: '鍙叆(checkBox)'}
-            ,{field: 'outEnable', align: 'center',title: '鍙嚭(checkBox)'}
-            ,{field: 'controlRows', align: 'center',title: '鎺у埗搴撲綅鎺掑彿'}
-            ,{field: 'deepRows', align: 'center',title: '娣卞簱浣嶆帓鍙�'}
-            ,{field: 'inStationList', align: 'center',title: '鍏ュ簱绔欏垪琛�'}
-            ,{field: 'outStationList', align: 'center',title: '鍑哄簱绔欏垪琛�'}
-            ,{field: 'maxInTask', align: 'center',title: '鏈�澶у叆搴撲换鍔℃暟'}
-            ,{field: 'maxOutTask', align: 'center',title: '鏈�澶у嚭搴撲换鍔℃暟'}
-            // ,{field: 'createBy', align: 'center',title: '鍒涘缓浜哄憳'}
-            // ,{field: 'createTime$', align: 'center',title: '鍒涘缓鏃堕棿'}
-            // ,{field: 'updateBy', align: 'center',title: '淇敼浜哄憳'}
-            // ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basCrnp)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basCrnp)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.crnNo;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basCrnp': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basCrnp/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basCrnp)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.crnNo]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basCrnp/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basCrnp/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basCrnpErr/basCrnpErr.js b/src/main/webapp/static/js/basCrnpErr/basCrnpErr.js
index 4784f1c..af76487 100644
--- a/src/main/webapp/static/js/basCrnpErr/basCrnpErr.js
+++ b/src/main/webapp/static/js/basCrnpErr/basCrnpErr.js
@@ -1,261 +1,913 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basCrnpErr';
+    var entityName = 'BasCrnpErr';
+    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: 'errorCode',
+        columnName: 'error_code',
+        label: '寮� 甯� 鐮�',
+        tableProp: 'errorCode',
+        exportField: 'errorCode',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'errName',
+        columnName: 'err_name',
+        label: '寮傘��銆�甯�',
+        tableProp: 'errName',
+        exportField: 'errName',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser$',
+        exportField: 'modiUser$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'user',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '娣诲姞浜哄憳',
+        tableProp: 'appeUser$',
+        exportField: 'appeUser$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'user',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basCrnpErr',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basCrnpErr/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'errorCode', align: 'center',title: '寮傚父鐮�'}
-            ,{field: 'errName', align: 'center',title: '寮傚父'}
-            ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'appeUser$', align: 'center',title: '娣诲姞浜哄憳'}
-            ,{field: 'appeTime$', align: 'center',title: '娣诲姞鏃堕棿'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basCrnpErr)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basCrnpErr)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basCrnpErr': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basCrnpErr/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basCrnpErr)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basCrnpErr/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basCrnpErr/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#modiTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['modiTime\\$']:null
-            });
-            layDate.render({
-                elem: '#appeTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['appeTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basCrnpErrLog/basCrnpErrLog.js b/src/main/webapp/static/js/basCrnpErrLog/basCrnpErrLog.js
index 4aaaad2..e6566d2 100644
--- a/src/main/webapp/static/js/basCrnpErrLog/basCrnpErrLog.js
+++ b/src/main/webapp/static/js/basCrnpErrLog/basCrnpErrLog.js
@@ -1,285 +1,1165 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basCrnpErrLog';
+    var entityName = 'BasCrnpErrLog';
+    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: 'crnNo',
+        columnName: 'crn_no',
+        label: '鍫嗗灈鏈哄彿',
+        tableProp: 'crnNo',
+        exportField: 'crnNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basCrnpErrLog',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basCrnpErrLog/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'startTime$', align: 'center',title: '鍙戠敓鏃堕棿'}
-            ,{field: 'endTime$', align: 'center',title: '缁撴潫鏃堕棿'}
-            ,{field: 'wrkSts$', align: 'center',title: '宸ヤ綔鐘舵��'}
-            ,{field: 'ioType$', align: 'center',title: '鍏ュ嚭搴撶被鍨�'}
-            ,{field: 'crnNo', align: 'center',title: '鍫嗗灈鏈哄彿'}
-            ,{field: 'locNo', align: 'center',title: '鐩爣搴撲綅'}
-            ,{field: 'staNo', align: 'center',title: '鐩爣绔�'}
-            ,{field: 'sourceStaNo', align: 'center',title: '婧愮珯'}
-            ,{field: 'sourceLocNo', align: 'center',title: '婧愬簱浣�'}
-            ,{field: 'barcode', align: 'center',title: '鏉$爜'}
-            ,{field: 'errCode', align: 'center',title: '寮傚父鐮�'}
-            ,{field: 'error', align: 'center',title: '寮傚父'}
-            ,{field: 'status$', align: 'center',title: '寮傚父鎯呭喌'}
-            ,{field: 'createTime$', align: 'center',title: '娣诲姞鏃堕棿'}
-            ,{field: 'createBy$', align: 'center',title: '娣诲姞浜哄憳'}
-            ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'updateBy$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
-            ,{field: 'systemStatus', align: 'center',title: '绯荤粺鐘舵�佹暟鎹�'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basCrnpErrLog)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basCrnpErrLog)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basCrnpErrLog': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basCrnpErrLog/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basCrnpErrLog)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basCrnpErrLog/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basCrnpErrLog/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#startTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['startTime\\$']:null
-            });
-            layDate.render({
-                elem: '#endTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['endTime\\$']:null
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basCrnpOpt/basCrnpOpt.js b/src/main/webapp/static/js/basCrnpOpt/basCrnpOpt.js
index 4fd5a85..1d686da 100644
--- a/src/main/webapp/static/js/basCrnpOpt/basCrnpOpt.js
+++ b/src/main/webapp/static/js/basCrnpOpt/basCrnpOpt.js
@@ -1,268 +1,1039 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basCrnpOpt';
+    var entityName = 'BasCrnpOpt';
+    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: 'crnNo',
+        columnName: 'crn_no',
+        label: '鍫嗗灈鏈哄彿',
+        tableProp: 'crnNo',
+        exportField: 'crnNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'sendTime',
+        columnName: 'send_time',
+        label: '涓嬪彂鏃堕棿',
+        tableProp: 'sendTime$',
+        exportField: 'sendTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'mode',
+        columnName: 'mode',
+        label: '浣溿��銆�涓�',
+        tableProp: 'mode',
+        exportField: 'mode',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sourceLocNo',
+        columnName: 'source_loc_no',
+        label: '璧风偣搴撲綅',
+        tableProp: 'sourceLocNo',
+        exportField: 'sourceLocNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'targetLocNo',
+        columnName: 'target_loc_no',
+        label: '鐩爣搴撲綅',
+        tableProp: 'targetLocNo',
+        exportField: 'targetLocNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'command',
+        columnName: 'command',
+        label: '鍛姐��銆�浠�',
+        tableProp: 'command',
+        exportField: 'command',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'send',
+        columnName: 'send',
+        label: '涓嬪彂鐘舵��',
+        tableProp: 'send$',
+        exportField: 'send$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鏈笅鍙�' }, { rawValue: '1', label: '宸蹭笅鍙�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '璇锋眰鍝嶅簲',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basCrnpOpt',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basCrnpOpt/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'crnNo', align: 'center',title: '鍫嗗灈鏈哄彿'}
-            ,{field: 'sendTime$', align: 'center',title: '涓嬪彂鏃堕棿'}
-            ,{field: 'mode', align: 'center',title: '浣滀笟'}
-            ,{field: 'sourceLocNo', align: 'center',title: '璧风偣搴撲綅'}
-            ,{field: 'targetLocNo', align: 'center',title: '鐩爣搴撲綅'}
-            ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'updateBy$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
-            ,{field: 'command', align: 'center',title: '鍛戒护'}
-            ,{field: 'systemStatus', align: 'center',title: '绯荤粺鐘舵��'}
-            ,{field: 'send$', align: 'center',title: '涓嬪彂鐘舵��'}
-            ,{field: 'response', align: 'center',title: '璇锋眰鍝嶅簲'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basCrnpOpt)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basCrnpOpt)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basCrnpOpt': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basCrnpOpt/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basCrnpOpt)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basCrnpOpt/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basCrnpOpt/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#sendTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['sendTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basDevp/basDevp.js b/src/main/webapp/static/js/basDevp/basDevp.js
index a373c0a..60dd95f 100644
--- a/src/main/webapp/static/js/basDevp/basDevp.js
+++ b/src/main/webapp/static/js/basDevp/basDevp.js
@@ -1,328 +1,2533 @@
-var pageCurr;
-layui
-  .config({
-    base: baseUrl + "/static/layui/lay/modules/",
-  })
-  .use(["table", "laydate", "form", "admin"], function () {
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basDevp';
+    var entityName = 'BasDevp';
+    var primaryKeyField = 'id';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'devNo',
+        columnName: 'dev_no',
+        label: '',
+        tableProp: 'devNo',
+        exportField: 'devNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'decDesc',
+        columnName: 'dec_desc',
+        label: '',
+        tableProp: 'decDesc',
+        exportField: 'decDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'devMk',
+        columnName: 'dev_mk',
+        label: '',
+        tableProp: 'devMk',
+        exportField: 'devMk',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inEnable',
+        columnName: 'in_enable',
+        label: '',
+        tableProp: 'inEnable',
+        exportField: 'inEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outEnable',
+        columnName: 'out_enable',
+        label: '',
+        tableProp: 'outEnable',
+        exportField: 'outEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'autoing',
+        columnName: 'autoing',
+        label: '',
+        tableProp: 'autoing',
+        exportField: 'autoing',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'loading',
+        columnName: 'loading',
+        label: '',
+        tableProp: 'loading',
+        exportField: 'loading',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'canining',
+        columnName: 'canining',
+        label: '',
+        tableProp: 'canining',
+        exportField: 'canining',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'canouting',
+        columnName: 'canouting',
+        label: '',
+        tableProp: 'canouting',
+        exportField: 'canouting',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'fronting',
+        columnName: 'fronting',
+        label: '',
+        tableProp: 'fronting',
+        exportField: 'fronting',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'rearing',
+        columnName: 'rearing',
+        label: '',
+        tableProp: 'rearing',
+        exportField: 'rearing',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'uping',
+        columnName: 'uping',
+        label: '',
+        tableProp: 'uping',
+        exportField: 'uping',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'downing',
+        columnName: 'downing',
+        label: '',
+        tableProp: 'downing',
+        exportField: 'downing',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inreq1',
+        columnName: 'inreq1',
+        label: '',
+        tableProp: 'inreq1',
+        exportField: 'inreq1',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inreq2',
+        columnName: 'inreq2',
+        label: '',
+        tableProp: 'inreq2',
+        exportField: 'inreq2',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'wrkNo',
+        columnName: 'wrk_no',
+        label: '',
+        tableProp: 'wrkNo',
+        exportField: 'wrkNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wrkNo1',
+        columnName: 'wrk_no1',
+        label: '',
+        tableProp: 'wrkNo1',
+        exportField: 'wrkNo1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ctnType',
+        columnName: 'ctn_type',
+        label: '',
+        tableProp: 'ctnType',
+        exportField: 'ctnType',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: 'inQty',
+        columnName: 'in_qty',
+        label: '',
+        tableProp: 'inQty',
+        exportField: 'inQty',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'row1',
+        columnName: 'row1',
+        label: '',
+        tableProp: 'row1',
+        exportField: 'row1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioTime',
+        columnName: 'io_time',
+        label: '',
+        tableProp: 'ioTime$',
+        exportField: 'ioTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'area',
+        columnName: 'area',
+        label: '',
+        tableProp: 'area',
+        exportField: 'area',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inOk',
+        columnName: 'in_ok',
+        label: '',
+        tableProp: 'inOk',
+        exportField: 'inOk',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outOk',
+        columnName: 'out_ok',
+        label: '',
+        tableProp: 'outOk',
+        exportField: 'outOk',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locType1',
+        columnName: 'loc_type1',
+        label: '',
+        tableProp: 'locType1',
+        exportField: 'locType1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType2',
+        columnName: 'loc_type2',
+        label: '',
+        tableProp: 'locType2',
+        exportField: 'locType2',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType3',
+        columnName: 'loc_type3',
+        label: '',
+        tableProp: 'locType3',
+        exportField: 'locType3',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'stdQty',
+        columnName: 'std_qty',
+        label: '',
+        tableProp: 'stdQty',
+        exportField: 'stdQty',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'minWt',
+        columnName: 'min_wt',
+        label: '',
+        tableProp: 'minWt',
+        exportField: 'minWt',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'maxWt',
+        columnName: 'max_wt',
+        label: '',
+        tableProp: 'maxWt',
+        exportField: 'maxWt',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'grossWt',
+        columnName: 'gross_wt',
+        label: '',
+        tableProp: 'grossWt',
+        exportField: 'grossWt',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'cartPos',
+        columnName: 'cart_pos',
+        label: '',
+        tableProp: 'cartPos',
+        exportField: 'cartPos',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'qrCodeValue',
+        columnName: 'qr_code_value',
+        label: '',
+        tableProp: 'qrCodeValue',
+        exportField: 'qrCodeValue',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'lev',
+        columnName: 'lev',
+        label: '',
+        tableProp: 'lev',
+        exportField: 'lev',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'liftNo',
+        columnName: 'lift_no',
+        label: '',
+        tableProp: 'liftNo',
+        exportField: 'liftNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'devNo',
+        columnName: 'dev_no',
+        label: '',
+        tableProp: 'devNo',
+        exportField: 'devNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'decDesc',
+        columnName: 'dec_desc',
+        label: '',
+        tableProp: 'decDesc',
+        exportField: 'decDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'devMk',
+        columnName: 'dev_mk',
+        label: '',
+        tableProp: 'devMk',
+        exportField: 'devMk',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inEnable',
+        columnName: 'in_enable',
+        label: '',
+        tableProp: 'inEnable',
+        exportField: 'inEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outEnable',
+        columnName: 'out_enable',
+        label: '',
+        tableProp: 'outEnable',
+        exportField: 'outEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'autoing',
+        columnName: 'autoing',
+        label: '',
+        tableProp: 'autoing',
+        exportField: 'autoing',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'loading',
+        columnName: 'loading',
+        label: '',
+        tableProp: 'loading',
+        exportField: 'loading',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'canining',
+        columnName: 'canining',
+        label: '',
+        tableProp: 'canining',
+        exportField: 'canining',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'canouting',
+        columnName: 'canouting',
+        label: '',
+        tableProp: 'canouting',
+        exportField: 'canouting',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'fronting',
+        columnName: 'fronting',
+        label: '',
+        tableProp: 'fronting',
+        exportField: 'fronting',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'rearing',
+        columnName: 'rearing',
+        label: '',
+        tableProp: 'rearing',
+        exportField: 'rearing',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'uping',
+        columnName: 'uping',
+        label: '',
+        tableProp: 'uping',
+        exportField: 'uping',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'downing',
+        columnName: 'downing',
+        label: '',
+        tableProp: 'downing',
+        exportField: 'downing',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inreq1',
+        columnName: 'inreq1',
+        label: '',
+        tableProp: 'inreq1',
+        exportField: 'inreq1',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inreq2',
+        columnName: 'inreq2',
+        label: '',
+        tableProp: 'inreq2',
+        exportField: 'inreq2',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'wrkNo',
+        columnName: 'wrk_no',
+        label: '',
+        tableProp: 'wrkNo',
+        exportField: 'wrkNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wrkNo1',
+        columnName: 'wrk_no1',
+        label: '',
+        tableProp: 'wrkNo1',
+        exportField: 'wrkNo1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ctnType',
+        columnName: 'ctn_type',
+        label: '',
+        tableProp: 'ctnType',
+        exportField: 'ctnType',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: 'inQty',
+        columnName: 'in_qty',
+        label: '',
+        tableProp: 'inQty',
+        exportField: 'inQty',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'row1',
+        columnName: 'row1',
+        label: '',
+        tableProp: 'row1',
+        exportField: 'row1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioTime',
+        columnName: 'io_time',
+        label: '',
+        tableProp: 'ioTime$',
+        exportField: 'ioTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'area',
+        columnName: 'area',
+        label: '',
+        tableProp: 'area',
+        exportField: 'area',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inOk',
+        columnName: 'in_ok',
+        label: '',
+        tableProp: 'inOk',
+        exportField: 'inOk',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outOk',
+        columnName: 'out_ok',
+        label: '',
+        tableProp: 'outOk',
+        exportField: 'outOk',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locType1',
+        columnName: 'loc_type1',
+        label: '',
+        tableProp: 'locType1',
+        exportField: 'locType1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType2',
+        columnName: 'loc_type2',
+        label: '',
+        tableProp: 'locType2',
+        exportField: 'locType2',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType3',
+        columnName: 'loc_type3',
+        label: '',
+        tableProp: 'locType3',
+        exportField: 'locType3',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'stdQty',
+        columnName: 'std_qty',
+        label: '',
+        tableProp: 'stdQty',
+        exportField: 'stdQty',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'minWt',
+        columnName: 'min_wt',
+        label: '',
+        tableProp: 'minWt',
+        exportField: 'minWt',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'maxWt',
+        columnName: 'max_wt',
+        label: '',
+        tableProp: 'maxWt',
+        exportField: 'maxWt',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'grossWt',
+        columnName: 'gross_wt',
+        label: '',
+        tableProp: 'grossWt',
+        exportField: 'grossWt',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'cartPos',
+        columnName: 'cart_pos',
+        label: '',
+        tableProp: 'cartPos',
+        exportField: 'cartPos',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'qrCodeValue',
+        columnName: 'qr_code_value',
+        label: '',
+        tableProp: 'qrCodeValue',
+        exportField: 'qrCodeValue',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'lev',
+        columnName: 'lev',
+        label: '',
+        tableProp: 'lev',
+        exportField: 'lev',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'liftNo',
+        columnName: 'lift_no',
+        label: '',
+        tableProp: 'liftNo',
+        exportField: 'liftNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: 'devpNo',
+        columnName: 'devp_no',
+        label: '璁惧缂栧彿',
+        tableProp: 'devpNo',
+        exportField: 'devpNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'createBy',
+        columnName: 'create_by',
+        label: '鍒涘缓浜哄憳',
+        tableProp: 'createBy',
+        exportField: 'createBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'updateBy',
+        columnName: 'update_by',
+        label: '淇敼浜哄憳',
+        tableProp: 'updateBy',
+        exportField: 'updateBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: '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: 'stationList',
+        columnName: 'station_list',
+        label: '绔欑偣鏁版嵁',
+        tableProp: 'stationList',
+        exportField: 'stationList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'barcodeStationList',
+        columnName: 'barcode_station_list',
+        label: '鏉$爜绔欑偣鏁版嵁',
+        tableProp: 'barcodeStationList',
+        exportField: 'barcodeStationList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 134,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inStationList',
+        columnName: 'in_station_list',
+        label: '鍏ュ簱绔欑偣鏁版嵁',
+        tableProp: 'inStationList',
+        exportField: 'inStationList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 134,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outStationList',
+        columnName: 'out_station_list',
+        label: '鍑哄簱绔欑偣鏁版嵁',
+        tableProp: 'outStationList',
+        exportField: 'outStationList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 134,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'runBlockReassignLocStationList',
+        columnName: 'run_block_reassign_loc_station_list',
+        label: '杩愯鍫靛閲嶆柊鍒嗛厤搴撲綅绔欑偣鏁版嵁',
+        tableProp: 'runBlockReassignLocStationList',
+        exportField: 'runBlockReassignLocStationList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'isOutOrderList',
+        columnName: 'is_out_order_list',
+        label: '璇疯緭鍏ュ嚭搴撴帓搴忎氦浜掔偣',
+        tableProp: 'isOutOrderList',
+        exportField: 'isOutOrderList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'isLiftTransferList',
+        columnName: 'is_lift_transfer_list',
+        label: '璇疯緭鍏ョ數姊腑杞偣',
+        tableProp: 'isLiftTransferList',
+        exportField: 'isLiftTransferList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-      elem: "#basDevp",
-      headers: { token: localStorage.getItem("token") },
-      url: baseUrl + "/basDevp/list/auth",
-      page: true,
-      limit: 15,
-      limits: [15, 30, 50, 100, 200, 500],
-      toolbar: "#toolbar",
-      cellMinWidth: 50,
-      height: "full-120",
-      cols: [
-        [
-          { type: "checkbox" },
-          { field: "id", align: "center", title: "缂栧彿" },
-          { field: "devpNo", align: "center", title: "璁惧缂栧彿" },
-          { field: "status$", align: "center", title: "鐘舵��" },
-          // { field: "createBy", align: "center", title: "鍒涘缓浜哄憳" },
-          // { field: "createTime$", align: "center", title: "鍒涘缓鏃堕棿" },
-          // { field: "updateBy", align: "center", title: "淇敼浜哄憳" },
-          // { field: "updateTime$", align: "center", title: "淇敼鏃堕棿" },
-          { field: "memo", align: "center", title: "澶囨敞" },
-          { field: "stationList", align: "center", title: "绔欑偣鏁版嵁" },
-          { field: "barcodeStationList", align: "center", title: "鏉$爜绔欑偣鏁版嵁" },
-          { field: "inStationList", align: "center", title: "鍏ュ簱绔欑偣鏁版嵁" },
-          { field: "outStationList", align: "center", title: "鍑哄簱绔欑偣鏁版嵁" },
-          { field: "runBlockReassignLocStationList", align: "center", title: "杩愯鍫靛閲嶆柊鍒嗛厤搴撲綅绔欑偣鏁版嵁" },
-          { field: "isOutOrderList", align: "center", title: "鍑哄簱鎺掑簭浜や簰鐐�" },
-          { field: "isLiftTransferList", align: "center", title: "椤跺崌绉绘牻鐐�" },
+    ]);
 
-          {
-            fixed: "right",
-            title: "鎿嶄綔",
-            align: "center",
-            toolbar: "#operate",
-            width: 240,
-          },
-        ],
-      ],
-      request: {
-        pageName: "curr",
-        pageSize: "limit",
-      },
-      parseData: function (res) {
-        return {
-          code: res.code,
-          msg: res.msg,
-          count: res.data.total,
-          data: res.data.records,
+    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: ''
         };
-      },
-      response: {
-        statusCode: 200,
-      },
-      done: function (res, curr, count) {
-        if (res.code === 403) {
-          top.location.href = baseUrl + "/";
+        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];
         }
-        pageCurr = curr;
-        limit();
-      },
-    });
+        return row ? row[field.field] : '';
+    }
 
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on("sort(basDevp)", function (obj) {
-      var searchData = {};
-      $.each($("#search-box [name]").serializeArray(), function () {
-        searchData[this.name] = this.value;
-      });
-      searchData["orderByField"] = obj.field;
-      searchData["orderByType"] = obj.type;
-      tableIns.reload({
-        where: searchData,
-        page: { curr: 1 },
-      });
-    });
+    function isCheckboxChecked(row, field) {
+        var value = row ? row[field.field] : null;
+        var activeValue = normalizeOptionValue(field, field.checkboxActiveRaw);
+        return String(value) === String(activeValue);
+    }
 
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on("toolbar(basDevp)", function (obj) {
-      var checkStatus = table.checkStatus(obj.config.id).data;
-      switch (obj.event) {
-        case "addData":
-          showEditModel();
-          break;
-        case "deleteData":
-          if (checkStatus.length === 0) {
-            layer.msg("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁", { icon: 2 });
-            return;
-          }
-          del(
-            checkStatus.map(function (d) {
-              return d.id;
-            })
-          );
-          break;
-        case "exportData":
-          admin.confirm("纭畾瀵煎嚭Excel鍚�", { shadeClose: true }, function () {
-            var titles = [];
-            var fields = [];
-            obj.config.cols[0].map(function (col) {
-              if (
-                col.type === "normal" &&
-                col.hide === false &&
-                col.toolbar == null
-              ) {
-                titles.push(col.title);
-                fields.push(col.field);
-              }
-            });
-            var exportData = {};
-            $.each($("#search-box [name]").serializeArray(), function () {
-              exportData[this.name] = this.value;
-            });
-            var param = {
-              basDevp: exportData,
-              fields: fields,
+    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);
             };
-            $.ajax({
-              url: baseUrl + "/basDevp/export/auth",
-              headers: { token: localStorage.getItem("token") },
-              data: JSON.stringify(param),
-              dataType: "json",
-              contentType: "application/json;charset=UTF-8",
-              method: "POST",
-              success: function (res) {
-                layer.closeAll();
-                if (res.code === 200) {
-                  table.exportFile(titles, res.data, "xls");
-                } else if (res.code === 403) {
-                  top.location.href = baseUrl + "/";
-                } else {
-                  layer.msg(res.msg, { icon: 2 });
-                }
-              },
-            });
-          });
-          break;
-      }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on("tool(basDevp)", function (obj) {
-      var data = obj.data;
-      switch (obj.event) {
-        case "edit":
-          showEditModel(data);
-          break;
-        case "del":
-          del([data.id]);
-          break;
-        case "initStation":
-          showInitStation(data);
-          break;
-      }
-    });
-
-    function showInitStation(mData) {
-      admin.open({
-        type: 1,
-        area: "600px",
-        title: "鍒濆鍖栫珯鐐规暟鎹�",
-        content: $("#initStationDialog").html(),
-        success: function (layero, dIndex) {
-          layDateRender(mData);
-          form.val("detail", mData);
-          form.on("submit(editSubmit)", function (data) {
-            var loadIndex = layer.load(2);
-            $.ajax({
-              url: baseUrl + "/basDevp/initStation/auth",
-              headers: { token: localStorage.getItem("token") },
-              contentType: "application/json;charset=UTF-8",
-              data: JSON.stringify(data.field),
-              method: "POST",
-              success: function (res) {
-                layer.close(loadIndex);
-                if (res.code === 200) {
-                  layer.close(dIndex);
-                  layer.msg(res.msg, { icon: 1 });
-                  tableReload();
-                } else if (res.code === 403) {
-                  top.location.href = baseUrl + "/";
-                } else {
-                  layer.msg(res.msg, { icon: 2 });
-                }
-              },
-            });
-            return false;
-          });
-          $(layero).children(".layui-layer-content").css("overflow", "visible");
-          layui.form.render("select");
         },
-      });
-    }
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-      admin.open({
-        type: 1,
-        area: "600px",
-        title: (mData ? "淇敼" : "娣诲姞") + "璁㈠崟鐘舵��",
-        content: $("#editDialog").html(),
-        success: function (layero, dIndex) {
-          layDateRender(mData);
-          form.val("detail", mData);
-          form.on("submit(editSubmit)", function (data) {
-            var loadIndex = layer.load(2);
+        fetchForeignSuggestions: function (field, queryString, callback) {
+            if (!field.foreignQuery || !queryString) {
+                callback([]);
+                return;
+            }
+            var self = this;
             $.ajax({
-              url: baseUrl + "/basDevp/" + (mData ? "update" : "add") + "/auth",
-              headers: { token: localStorage.getItem("token") },
-              data: data.field,
-              method: "POST",
-              success: function (res) {
-                layer.close(loadIndex);
-                if (res.code === 200) {
-                  layer.close(dIndex);
-                  layer.msg(res.msg, { icon: 1 });
-                  tableReload();
-                } else if (res.code === 403) {
-                  top.location.href = baseUrl + "/";
-                } else {
-                  layer.msg(res.msg, { icon: 2 });
+                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([]);
                 }
-              },
             });
-            return false;
-          });
-          $(layero).children(".layui-layer-content").css("overflow", "visible");
-          layui.form.render("select");
         },
-      });
-    }
-
-    /* 鍒犻櫎 */
-    function del(ids) {
-      layer.confirm(
-        "纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵",
-        {
-          skin: "layui-layer-admin",
-          shade: 0.1,
+        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) : '');
         },
-        function (i) {
-          layer.close(i);
-          var loadIndex = layer.load(2);
-          $.ajax({
-            url: baseUrl + "/basDevp/delete/auth",
-            headers: { token: localStorage.getItem("token") },
-            data: { ids: ids },
-            method: "POST",
-            success: function (res) {
-              layer.close(loadIndex);
-              if (res.code === 200) {
-                layer.msg(res.msg, { icon: 1 });
-                tableReload();
-              } else if (res.code === 403) {
-                top.location.href = baseUrl + "/";
-              } else {
-                layer.msg(res.msg, { icon: 2 });
-              }
-            },
-          });
+        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('瀵煎嚭澶辫触');
+                        }
+                    });
+                }
+            })
+        });
     }
 
-    // 鎼滅储
-    form.on("submit(search)", function (data) {
-      pageCurr = 1;
-      tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on("submit(reset)", function (data) {
-      pageCurr = 1;
-      clearFormVal($("#search-box"));
-      tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-      setTimeout(function () {
-        layDate.render({
-          elem: ".layui-laydate-range",
-          type: "datetime",
-          range: true,
-        });
-        layDate.render({
-          elem: "#createTime\\$",
-          type: "datetime",
-          value: data !== undefined ? data["createTime\\$"] : null,
-        });
-        layDate.render({
-          elem: "#updateTime\\$",
-          type: "datetime",
-          value: data !== undefined ? data["updateTime\\$"] : null,
-        });
-      }, 300);
-    }
-    layDateRender();
-  });
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on("click", "#data-detail-close", function () {
-  parent.layer.closeAll();
-});
-
-function tableReload(child) {
-  var searchData = {};
-  $.each($("#search-box [name]").serializeArray(), function () {
-    searchData[this.name] = this.value;
-  });
-  tableIns.reload({
-    where: searchData,
-    page: { curr: pageCurr },
-  });
-}
+})();
diff --git a/src/main/webapp/static/js/basDualCrnp/basDualCrnp.js b/src/main/webapp/static/js/basDualCrnp/basDualCrnp.js
index 9202558..b6e5e77 100644
--- a/src/main/webapp/static/js/basDualCrnp/basDualCrnp.js
+++ b/src/main/webapp/static/js/basDualCrnp/basDualCrnp.js
@@ -1,272 +1,1111 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basDualCrnp';
+    var entityName = 'BasDualCrnp';
+    var primaryKeyField = 'crnNo';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'crnNo',
+        columnName: 'crn_no',
+        label: '缂栥��銆�鍙�',
+        tableProp: 'crnNo',
+        exportField: 'crnNo',
+        kind: 'text',
+        valueType: 'number',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        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: 'inEnable',
+        columnName: 'in_enable',
+        label: '鍙叆(checkBox)',
+        tableProp: 'inEnable',
+        exportField: 'inEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outEnable',
+        columnName: 'out_enable',
+        label: '鍙嚭(checkBox)',
+        tableProp: 'outEnable',
+        exportField: 'outEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'createBy',
+        columnName: 'create_by',
+        label: '鍒涘缓浜哄憳',
+        tableProp: 'createBy',
+        exportField: 'createBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'updateBy',
+        columnName: 'update_by',
+        label: '淇敼浜哄憳',
+        tableProp: 'updateBy',
+        exportField: 'updateBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: '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: 'controlRows',
+        columnName: 'control_rows',
+        label: '鎺у埗搴撲綅鎺掑彿',
+        tableProp: 'controlRows',
+        exportField: 'controlRows',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 134,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'deepRows',
+        columnName: 'deep_rows',
+        label: '娣卞簱浣嶆帓鍙�',
+        tableProp: 'deepRows',
+        exportField: 'deepRows',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'inStationList',
+        columnName: 'in_station_list',
+        label: '鍏ュ簱绔欑偣',
+        tableProp: 'inStationList',
+        exportField: 'inStationList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outStationList',
+        columnName: 'out_station_list',
+        label: '鍑哄簱绔欑偣',
+        tableProp: 'outStationList',
+        exportField: 'outStationList',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'maxInTask',
+        columnName: 'max_in_task',
+        label: '鏈�澶у叆搴撲换鍔℃暟',
+        tableProp: 'maxInTask',
+        exportField: 'maxInTask',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 152,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'maxOutTask',
+        columnName: 'max_out_task',
+        label: '鏈�澶у嚭搴撲换鍔℃暟',
+        tableProp: 'maxOutTask',
+        exportField: 'maxOutTask',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 152,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'disableStationOneBays',
+        columnName: 'disable_station_one_bays',
+        label: '宸ヤ綅1绂佹鎵ц鍒�',
+        tableProp: 'disableStationOneBays',
+        exportField: 'disableStationOneBays',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 170,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'disableStationTwoBays',
+        columnName: 'disable_station_two_bays',
+        label: '宸ヤ綅2绂佹鎵ц鍒�',
+        tableProp: 'disableStationTwoBays',
+        exportField: 'disableStationTwoBays',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 170,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basDualCrnp',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basDualCrnp/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'crnNo', align: 'center',title: '缂栧彿'}
-            ,{field: 'status$', align: 'center',title: '鐘舵��'}
-            // ,{field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'inEnable', align: 'center',title: '鍙叆(checkBox)'}
-            ,{field: 'outEnable', align: 'center',title: '鍙嚭(checkBox)'}
-            // ,{field: 'createBy', align: 'center',title: '鍒涘缓浜哄憳'}
-            // ,{field: 'createTime$', align: 'center',title: '鍒涘缓鏃堕棿'}
-            // ,{field: 'updateBy', align: 'center',title: '淇敼浜哄憳'}
-            // ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
-            ,{field: 'controlRows', align: 'center',title: '鎺у埗搴撲綅鎺掑彿'}
-            ,{field: 'deepRows', align: 'center',title: '娣卞簱浣嶆帓鍙�'}
-            ,{field: 'inStationList', align: 'center',title: '鍏ュ簱绔欑偣'}
-            ,{field: 'outStationList', align: 'center',title: '鍑哄簱绔欑偣'}
-            ,{field: 'maxInTask', align: 'center',title: '鏈�澶у叆搴撲换鍔℃暟'}
-            ,{field: 'maxOutTask', align: 'center',title: '鏈�澶у嚭搴撲换鍔℃暟'}
-            ,{field: 'disableStationOneBays', align: 'center',title: '宸ヤ綅1绂佹鎵ц鍒�'}
-            ,{field: 'disableStationTwoBays', align: 'center',title: '宸ヤ綅2绂佹鎵ц鍒�'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basDualCrnp)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basDualCrnp)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.crnNo;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basDualCrnp': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basDualCrnp/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basDualCrnp)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.crnNo]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basDualCrnp/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basDualCrnp/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basDualCrnpErr/basDualCrnpErr.js b/src/main/webapp/static/js/basDualCrnpErr/basDualCrnpErr.js
index 80f4079..6f27d97 100644
--- a/src/main/webapp/static/js/basDualCrnpErr/basDualCrnpErr.js
+++ b/src/main/webapp/static/js/basDualCrnpErr/basDualCrnpErr.js
@@ -1,261 +1,913 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basDualCrnpErr';
+    var entityName = 'BasDualCrnpErr';
+    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: 'errorCode',
+        columnName: 'error_code',
+        label: '寮� 甯� 鐮�',
+        tableProp: 'errorCode',
+        exportField: 'errorCode',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'errName',
+        columnName: 'err_name',
+        label: '寮傘��銆�甯�',
+        tableProp: 'errName',
+        exportField: 'errName',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser$',
+        exportField: 'modiUser$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'user',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '娣诲姞浜哄憳',
+        tableProp: 'appeUser$',
+        exportField: 'appeUser$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'user',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basDualCrnpErr',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basDualCrnpErr/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'errorCode', align: 'center',title: '寮傚父鐮�'}
-            ,{field: 'errName', align: 'center',title: '寮傚父'}
-            ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'appeUser$', align: 'center',title: '娣诲姞浜哄憳'}
-            ,{field: 'appeTime$', align: 'center',title: '娣诲姞鏃堕棿'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basDualCrnpErr)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basDualCrnpErr)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basDualCrnpErr': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basDualCrnpErr/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basDualCrnpErr)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basDualCrnpErr/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basDualCrnpErr/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#modiTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['modiTime\\$']:null
-            });
-            layDate.render({
-                elem: '#appeTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['appeTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basDualCrnpErrLog/basDualCrnpErrLog.js b/src/main/webapp/static/js/basDualCrnpErrLog/basDualCrnpErrLog.js
index 255c913..aa76c59 100644
--- a/src/main/webapp/static/js/basDualCrnpErrLog/basDualCrnpErrLog.js
+++ b/src/main/webapp/static/js/basDualCrnpErrLog/basDualCrnpErrLog.js
@@ -1,285 +1,1165 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basDualCrnpErrLog';
+    var entityName = 'BasDualCrnpErrLog';
+    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: 'crnNo',
+        columnName: 'crn_no',
+        label: '鍫嗗灈鏈哄彿',
+        tableProp: 'crnNo',
+        exportField: 'crnNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basDualCrnpErrLog',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basDualCrnpErrLog/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'startTime$', align: 'center',title: '鍙戠敓鏃堕棿'}
-            ,{field: 'endTime$', align: 'center',title: '缁撴潫鏃堕棿'}
-            ,{field: 'wrkSts$', align: 'center',title: '宸ヤ綔鐘舵��'}
-            ,{field: 'ioType$', align: 'center',title: '鍏ュ嚭搴撶被鍨�'}
-            ,{field: 'crnNo', align: 'center',title: '鍫嗗灈鏈哄彿'}
-            ,{field: 'locNo', align: 'center',title: '鐩爣搴撲綅'}
-            ,{field: 'staNo', align: 'center',title: '鐩爣绔�'}
-            ,{field: 'sourceStaNo', align: 'center',title: '婧愮珯'}
-            ,{field: 'sourceLocNo', align: 'center',title: '婧愬簱浣�'}
-            ,{field: 'barcode', align: 'center',title: '鏉$爜'}
-            ,{field: 'errCode', align: 'center',title: '寮傚父鐮�'}
-            ,{field: 'error', align: 'center',title: '寮傚父'}
-            ,{field: 'status$', align: 'center',title: '寮傚父鎯呭喌'}
-            ,{field: 'createTime$', align: 'center',title: '娣诲姞鏃堕棿'}
-            ,{field: 'createBy$', align: 'center',title: '娣诲姞浜哄憳'}
-            ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'updateBy$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
-            ,{field: 'systemStatus', align: 'center',title: '绯荤粺鐘舵�佹暟鎹�'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basDualCrnpErrLog)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basDualCrnpErrLog)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basDualCrnpErrLog': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basDualCrnpErrLog/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basDualCrnpErrLog)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basDualCrnpErrLog/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basDualCrnpErrLog/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#startTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['startTime\\$']:null
-            });
-            layDate.render({
-                elem: '#endTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['endTime\\$']:null
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basDualCrnpOpt/basDualCrnpOpt.js b/src/main/webapp/static/js/basDualCrnpOpt/basDualCrnpOpt.js
index d81383f..8a8fdc8 100644
--- a/src/main/webapp/static/js/basDualCrnpOpt/basDualCrnpOpt.js
+++ b/src/main/webapp/static/js/basDualCrnpOpt/basDualCrnpOpt.js
@@ -1,268 +1,1039 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basDualCrnpOpt';
+    var entityName = 'BasDualCrnpOpt';
+    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: 'crnNo',
+        columnName: 'crn_no',
+        label: '鍫嗗灈鏈哄彿',
+        tableProp: 'crnNo',
+        exportField: 'crnNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'sendTime',
+        columnName: 'send_time',
+        label: '涓嬪彂鏃堕棿',
+        tableProp: 'sendTime$',
+        exportField: 'sendTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'mode',
+        columnName: 'mode',
+        label: '浣溿��銆�涓�',
+        tableProp: 'mode',
+        exportField: 'mode',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sourceLocNo',
+        columnName: 'source_loc_no',
+        label: '璧风偣搴撲綅',
+        tableProp: 'sourceLocNo',
+        exportField: 'sourceLocNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'targetLocNo',
+        columnName: 'target_loc_no',
+        label: '鐩爣搴撲綅',
+        tableProp: 'targetLocNo',
+        exportField: 'targetLocNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'command',
+        columnName: 'command',
+        label: '鍛姐��銆�浠�',
+        tableProp: 'command',
+        exportField: 'command',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'send',
+        columnName: 'send',
+        label: '涓嬪彂鐘舵��',
+        tableProp: 'send$',
+        exportField: 'send$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鏈笅鍙�' }, { rawValue: '1', label: '宸蹭笅鍙�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '璇锋眰鍝嶅簲',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basDualCrnpOpt',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basDualCrnpOpt/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'crnNo', align: 'center',title: '鍫嗗灈鏈哄彿'}
-            ,{field: 'sendTime$', align: 'center',title: '涓嬪彂鏃堕棿'}
-            ,{field: 'mode', align: 'center',title: '浣滀笟'}
-            ,{field: 'sourceLocNo', align: 'center',title: '璧风偣搴撲綅'}
-            ,{field: 'targetLocNo', align: 'center',title: '鐩爣搴撲綅'}
-            ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'updateBy$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
-            ,{field: 'command', align: 'center',title: '鍛戒护'}
-            ,{field: 'systemStatus', align: 'center',title: '绯荤粺鐘舵��'}
-            ,{field: 'send$', align: 'center',title: '涓嬪彂鐘舵��'}
-            ,{field: 'response', align: 'center',title: '璇锋眰鍝嶅簲'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basDualCrnpOpt)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basDualCrnpOpt)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basDualCrnpOpt': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basDualCrnpOpt/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basDualCrnpOpt)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basDualCrnpOpt/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basDualCrnpOpt/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#sendTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['sendTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basLocSts/basLocSts.js b/src/main/webapp/static/js/basLocSts/basLocSts.js
index 47ac4ca..8c97d32 100644
--- a/src/main/webapp/static/js/basLocSts/basLocSts.js
+++ b/src/main/webapp/static/js/basLocSts/basLocSts.js
@@ -1,440 +1,1111 @@
-var pageCurr;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
+(function () {
+    var simpleEntityName = 'basLocSts';
+    var entityName = 'BasLocSts';
+    var primaryKeyField = 'locSts';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'locSts',
+        columnName: 'loc_sts',
+        label: '搴撲綅鐘舵�佷唬鍙�',
+        tableProp: 'locSts',
+        exportField: 'locSts',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locDesc',
+        columnName: 'loc_desc',
+        label: '搴撲綅鐘舵�佹弿杩�',
+        tableProp: 'locDesc',
+        exportField: 'locDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locSts',
+        columnName: 'loc_sts',
+        label: '搴撲綅鐘舵�佷唬鍙�',
+        tableProp: 'locSts',
+        exportField: 'locSts',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locDesc',
+        columnName: 'loc_desc',
+        label: '搴撲綅鐘舵�佹弿杩�',
+        tableProp: 'locDesc',
+        exportField: 'locDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locSts',
+        columnName: 'loc_sts',
+        label: '搴撲綅鐘舵�佷唬鍙�',
+        tableProp: 'locSts',
+        exportField: 'locSts',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locDesc',
+        columnName: 'loc_desc',
+        label: '搴撲綅鐘舵�佹弿杩�',
+        tableProp: 'locDesc',
+        exportField: 'locDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basLocSts',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basLocSts/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        even: true,
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {type: 'checkbox', fixed: 'left'}
-//            ,{field: 'id', title: 'ID', sort: true,align: 'center', fixed: 'left', width: 80}
-            ,{field: 'locSts', align: 'center',sort:true,title: '搴撲綅鐘舵�佷唬鍙�'}
-            ,{field: 'locDesc', align: 'center',sort: true,title: '搴撲綅鐘舵�佹弿杩�'}
-            ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿'}
-            // ,{field: 'appeUser$', align: 'center',title: '鍒涘缓鑰�',event: 'appeUser', style: 'text-decoration: underline;cursor:pointer'}
-            // ,{field: 'appeTime$', align: 'center',title: '娣诲姞鏃堕棿'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:150}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basLocSts)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
+        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();
         });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
+    }
+
+    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;
+    }
 
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basLocSts)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: ['500px', top.detailHeight],
-                    shadeClose: false,
-                    content: 'basLocSts_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                if (data.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(data.length===1?'姝�':data.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/basLocSts/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {param: JSON.stringify(data)},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basLocSts': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basLocSts/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
+    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;
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basLocSts)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'basLocSts_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit-save,#prompt', index).hide();
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: ['500px', top.detailHeight],
-                    shadeClose: false,
-                    content: 'basLocSts_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-save', index).hide();
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        top.convertDisabled(layer.getChildFrame('#locSts', index), true);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            case 'modiUser':
-                var param = top.reObject(data).modiUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '淇敼璇︽儏',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#password,#createTime\\$,#status', index).parent().parent().hide();
-                                       layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-            case 'appeUser':
-                var param = top.reObject(data).appeUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '鍒涜鎯�',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-
+        if (rawValue === '') {
+            return '';
         }
-    });
-
-    // 鏁版嵁淇濆瓨鍔ㄤ綔
-    form.on('submit(save)', function () {
-        if (banMsg != null){
-            layer.msg(banMsg);
-            return;
+        if (field && field.valueType === 'number') {
+            var numberVal = Number(rawValue);
+            return isNaN(numberVal) ? rawValue : numberVal;
         }
-        method("add");
-    });
+        return String(rawValue);
+    }
 
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        method("update")
-    });
+    function isSearchableField(field) {
+        return !!field && field.kind !== 'image' && !field.textarea;
+    }
 
-    function method(name){
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-//            id: $('#id').val(),
-            locSts: $('#locSts').val(),
-            locDesc: $('#locDesc').val(),
-            modiUser: $('#modiUser').val(),
-            modiTime: top.strToDate($('#modiTime\\$').val()),
-            appeUser: $('#appeUser').val(),
-            appeTime: top.strToDate($('#appeTime\\$').val()),
+    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: ''
         };
-        $.ajax({
-            url: baseUrl+"/basLocSts/"+name+"/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    tableReload(true);
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
-                }
-                layer.close(index);
+        fieldMeta.forEach(function (field) {
+            if (!isSearchableField(field)) {
+                return;
             }
-        })
+            result[field.field] = defaultSearchFieldValue(field);
+        });
+        return result;
     }
 
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
+    function createSearchDisplayDefaults() {
+        var result = {};
+        fieldMeta.forEach(function (field) {
+            if (field.kind === 'foreign' && isSearchableField(field)) {
+                result[field.field] = '';
+            }
+        });
+        return result;
+    }
 
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
+    function createDefaultVisibleColumnKeys() {
+        return fieldMeta.map(function (field) {
+            return field.field;
+        });
+    }
 
-    // 鏃堕棿閫夋嫨鍣�
-    layDate.render({
-        elem: '#modiTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#appeTime\\$',
-        type: 'datetime'
-    });
+    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;
+    }
 
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
+    function getTableValue(row, field) {
+        var prop = field.tableProp || field.field;
+        if (row && !isEmptyValue(row[prop])) {
+            return row[prop];
+        }
+        return row ? row[field.field] : '';
+    }
 
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
+    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);
+            };
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+        fetchForeignSuggestions: function (field, queryString, callback) {
+            if (!field.foreignQuery || !queryString) {
+                callback([]);
+                return;
             }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
-                    page: {
-                        curr: pageCurr-1
+            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;
                     }
-                });
-                pageCurr -= 1;
-            }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
+                    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, '');
         }
-    }
-}
+    };
 
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.9);
+    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('瀵煎嚭澶辫触');
+                        }
+                    });
+                }
+            })
+        });
     }
-    layer.style(index, {
-//        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
-    });
-}
 
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/basMap/basMap.js b/src/main/webapp/static/js/basMap/basMap.js
index 8d08c31..bc41975 100644
--- a/src/main/webapp/static/js/basMap/basMap.js
+++ b/src/main/webapp/static/js/basMap/basMap.js
@@ -1,323 +1,1219 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table', 'laydate', 'form', 'admin', 'upload'], function () {
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
-    var upload = layui.upload;
+(function () {
+    var simpleEntityName = 'basMap';
+    var entityName = 'BasMap';
+    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: 'data',
+        columnName: 'data',
+        label: '瀹炴椂鏁版嵁',
+        tableProp: 'data',
+        exportField: 'data',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: '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: 'lastData',
+        columnName: 'last_data',
+        label: '鏈�杩戞暟鎹�',
+        tableProp: 'lastData',
+        exportField: 'lastData',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'lev',
+        columnName: 'lev',
+        label: '灞傛暟',
+        tableProp: 'lev',
+        exportField: 'lev',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: 'data',
+        columnName: 'data',
+        label: '瀹炴椂鏁版嵁',
+        tableProp: 'data',
+        exportField: 'data',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: '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: 'lastData',
+        columnName: 'last_data',
+        label: '鏈�杩戞暟鎹�',
+        tableProp: 'lastData',
+        exportField: 'lastData',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'lev',
+        columnName: 'lev',
+        label: '灞傛暟',
+        tableProp: 'lev',
+        exportField: 'lev',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'originData',
+        columnName: 'origin_data',
+        label: '鍘熷鍦板浘',
+        tableProp: 'originData',
+        exportField: 'originData',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'data',
+        columnName: 'data',
+        label: '瀹炴椂鏁版嵁',
+        tableProp: 'data',
+        exportField: 'data',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: '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: 'lastData',
+        columnName: 'last_data',
+        label: '鏈�杩戞暟鎹�',
+        tableProp: 'lastData',
+        exportField: 'lastData',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'lev',
+        columnName: 'lev',
+        label: '灞傛暟',
+        tableProp: 'lev',
+        exportField: 'lev',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'originData',
+        columnName: 'origin_data',
+        label: '鍘熷鍦板浘',
+        tableProp: 'originData',
+        exportField: 'originData',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'baseRow',
+        columnName: 'base_row',
+        label: '鍩哄噯鎺�',
+        tableProp: 'baseRow',
+        exportField: 'baseRow',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'baseRowCode',
+        columnName: 'base_row_code',
+        label: '鍩哄噯鎺�-code',
+        tableProp: 'baseRowCode',
+        exportField: 'baseRowCode',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'baseBay',
+        columnName: 'base_bay',
+        label: '鍩哄噯鍒�',
+        tableProp: 'baseBay',
+        exportField: 'baseBay',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'baseBayCode',
+        columnName: 'base_bay_code',
+        label: '鍩哄噯鍒�-code',
+        tableProp: 'baseBayCode',
+        exportField: 'baseBayCode',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basMap',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl + '/basMap/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            // ,{field: 'id', align: 'center',title: '#'}
-            , {field: 'lev', align: 'center', title: '灞傛暟'}
-            , {field: 'data', align: 'center', title: '瀹炴椂鏁版嵁'}
-            , {field: 'createTime$', align: 'center', title: '鍒涘缓鏃堕棿'}
-            , {field: 'updateTime$', align: 'center', title: '鏇存柊鏃堕棿'}
-            , {field: 'lastData', align: 'center', title: '鏈�杩戞暟鎹�'}
-            , {field: 'originData', align: 'center', title: '鍘熷鍦板浘'}
-            // , {field: 'baseRow', align: 'center', title: '鍩哄噯鎺�'}
-            // , {field: 'baseRowCode', align: 'center', title: '鍩哄噯鎺�-code'}
-            // , {field: 'baseBay', align: 'center', title: '鍩哄噯鍒�'}
-            // , {field: 'baseBayCode', align: 'center', title: '鍩哄噯鍒�-code'}
+    ]);
 
-            , {fixed: 'right', title: '鎿嶄綔', align: 'center', toolbar: '#operate', width: 120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl + "/";
-            }
-            pageCurr = curr;
-            limit();
-            renderUpload();
+    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();
+        });
+    }
 
-    // 娓叉煋
-    var loadIndex;
-    function renderUpload() {
-        upload.render({
-            elem: '.demo-class-accept', // 缁戝畾澶氫釜鍏冪礌
-            url: baseUrl + "/basMap/crn/upload", // 姝ゅ閰嶇疆浣犺嚜宸辩殑涓婁紶鎺ュ彛鍗冲彲
-            accept: 'file', // 鏅�氭枃浠�
-            before: function (obj) {
-                loadIndex = layer.load(2);
-            },
-            done: function (res) {
-                layer.close(loadIndex);
-                if (res.code == 200) {
-                    layer.msg('瀵煎叆鎴愬姛');
-                    tableReload()
-                }else {
-                    layer.msg(res.msg, {icon: 2})
-                }
-            },
-            error: function () {
-                layer.close(loadIndex);
-                layer.msg('涓婁紶澶辫触', {icon: 2});
+    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;
     }
 
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basMap)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function () {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basMap)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch (obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-                if (checkStatus.length === 0) {
-                    layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                    return;
-                }
-                del(checkStatus.map(function (d) {
-                    return d.id;
-                }));
-                break;
-            case 'initLocMast':
-                initLocMast()
-                break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function () {
-                    var titles = [];
-                    var fields = [];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function () {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basMap': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl + "/basMap/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType: 'json',
-                        contentType: 'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles, res.data, 'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl + "/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
-        }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basMap)', function (obj) {
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    function initLocMast() {
-        layer.prompt({title: '璇疯緭鍏ュ垵濮嬪寲搴撲綅灞傛暟', formType: 0,   shadeClose: true}, function(lev, idx){
-            var loadIndex = layer.load(2);
-            $.ajax({
-                url: baseUrl + "/locMast/init",
-                headers: {'token': localStorage.getItem('token')},
-                data: {
-                    'lev': lev
-                },
-                method: 'POST',
-                success: function (res) {
-                    layer.close(loadIndex);
-                    layer.close(idx);
-                    if (res.code === 200) {
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403) {
-                        top.location.href = baseUrl + "/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
-                    }
-                }
-            })
+    function createDefaultVisibleColumnKeys() {
+        return fieldMeta.map(function (field) {
+            return field.field;
         });
     }
 
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl + "/basMap/" + (mData ? 'update' : 'add') + "/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200) {
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl + "/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
+    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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
-            $.ajax({
-                url: baseUrl + "/basMap/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
-                success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200) {
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403) {
-                        top.location.href = baseUrl + "/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
-                    }
+    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]);
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
+    function resolveSearchParam(field) {
+        if (field.kind === 'date' && field.columnName) {
+            return field.columnName;
+        }
+        return field.field;
+    }
 
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
+    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 () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                , type: 'datetime'
-                , range: true
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data !== undefined ? data['createTime\\$'] : null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data !== undefined ? data['updateTime\\$'] : null
-            });
-
-        }, 300);
+            URL.revokeObjectURL(anchor.href);
+            document.body.removeChild(anchor);
+        }, 0);
     }
 
-    layDateRender();
+    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')
+        };
+    }
 
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
+    function handleForbidden(res) {
+        if (res && res.code === 403) {
+            top.location.href = baseUrl + '/';
+            return true;
+        }
+        return false;
+    }
 
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+    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/basRgv/basRgv.js b/src/main/webapp/static/js/basRgv/basRgv.js
index cacf780..5d83f5d 100644
--- a/src/main/webapp/static/js/basRgv/basRgv.js
+++ b/src/main/webapp/static/js/basRgv/basRgv.js
@@ -1,262 +1,931 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basRgv';
+    var entityName = 'BasRgv';
+    var primaryKeyField = 'rgvNo';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'rgvNo',
+        columnName: 'rgv_no',
+        label: '缂栥��銆�鍙�',
+        tableProp: 'rgvNo',
+        exportField: 'rgvNo',
+        kind: 'text',
+        valueType: 'number',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'taskNo',
+        columnName: 'task_no',
+        label: '宸� 浣� 鍙�',
+        tableProp: 'taskNo',
+        exportField: 'taskNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'createBy',
+        columnName: 'create_by',
+        label: '鍒涘缓浜哄憳',
+        tableProp: 'createBy',
+        exportField: 'createBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'updateBy',
+        columnName: 'update_by',
+        label: '淇敼浜哄憳',
+        tableProp: 'updateBy',
+        exportField: 'updateBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: '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'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basRgv',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basRgv/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'rgvNo', align: 'center',title: '缂栧彿'}
-            ,{field: 'status$', align: 'center',title: '鐘舵��'}
-            ,{field: 'taskNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'createBy', align: 'center',title: '鍒涘缓浜哄憳'}
-            ,{field: 'createTime$', align: 'center',title: '鍒涘缓鏃堕棿'}
-            ,{field: 'updateBy', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basRgv)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basRgv)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.rgvNo;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basRgv': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basRgv/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basRgv)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.rgvNo]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + 'RGV',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basRgv/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basRgv/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basRgvErr/basRgvErr.js b/src/main/webapp/static/js/basRgvErr/basRgvErr.js
index 34c5b06..75ee751 100644
--- a/src/main/webapp/static/js/basRgvErr/basRgvErr.js
+++ b/src/main/webapp/static/js/basRgvErr/basRgvErr.js
@@ -1,261 +1,913 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basRgvErr';
+    var entityName = 'BasRgvErr';
+    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: 'errorCode',
+        columnName: 'error_code',
+        label: '寮� 甯� 鐮�',
+        tableProp: 'errorCode',
+        exportField: 'errorCode',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'errName',
+        columnName: 'err_name',
+        label: '寮傘��銆�甯�',
+        tableProp: 'errName',
+        exportField: 'errName',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser$',
+        exportField: 'modiUser$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'user',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '娣诲姞浜哄憳',
+        tableProp: 'appeUser$',
+        exportField: 'appeUser$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'user',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basRgvErr',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basRgvErr/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'errorCode', align: 'center',title: '寮傚父鐮�'}
-            ,{field: 'errName', align: 'center',title: '寮傚父'}
-            ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'appeUser$', align: 'center',title: '娣诲姞浜哄憳'}
-            ,{field: 'appeTime$', align: 'center',title: '娣诲姞鏃堕棿'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basRgvErr)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basRgvErr)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basRgvErr': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basRgvErr/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basRgvErr)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basRgvErr/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basRgvErr/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#modiTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['modiTime\\$']:null
-            });
-            layDate.render({
-                elem: '#appeTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['appeTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basRgvErrLog/basRgvErrLog.js b/src/main/webapp/static/js/basRgvErrLog/basRgvErrLog.js
index b163e8f..c448516 100644
--- a/src/main/webapp/static/js/basRgvErrLog/basRgvErrLog.js
+++ b/src/main/webapp/static/js/basRgvErrLog/basRgvErrLog.js
@@ -1,285 +1,1165 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basRgvErrLog';
+    var entityName = 'BasRgvErrLog';
+    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: 'taskNo',
+        columnName: 'task_no',
+        label: '宸� 浣� 鍙�',
+        tableProp: 'taskNo',
+        exportField: 'taskNo',
+        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: 'rgvNo',
+        columnName: 'rgv_no',
+        label: 'RGV鍙�',
+        tableProp: 'rgvNo',
+        exportField: 'rgvNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basRgvErrLog',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basRgvErrLog/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'taskNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'startTime$', align: 'center',title: '鍙戠敓鏃堕棿'}
-            ,{field: 'endTime$', align: 'center',title: '缁撴潫鏃堕棿'}
-            ,{field: 'wrkSts$', align: 'center',title: '宸ヤ綔鐘舵��'}
-            ,{field: 'ioType$', align: 'center',title: '鍏ュ嚭搴撶被鍨�'}
-            ,{field: 'rgvNo', align: 'center',title: 'RGV鍙�'}
-            ,{field: 'locNo', align: 'center',title: '鐩爣搴撲綅'}
-            ,{field: 'staNo', align: 'center',title: '鐩爣绔�'}
-            ,{field: 'sourceStaNo', align: 'center',title: '婧愮珯'}
-            ,{field: 'sourceLocNo', align: 'center',title: '婧愬簱浣�'}
-            ,{field: 'barcode', align: 'center',title: '鏉$爜'}
-            ,{field: 'errCode', align: 'center',title: '寮傚父鐮�'}
-            ,{field: 'error', align: 'center',title: '寮傚父'}
-            ,{field: 'status$', align: 'center',title: '寮傚父鎯呭喌'}
-            ,{field: 'createTime$', align: 'center',title: '娣诲姞鏃堕棿'}
-            ,{field: 'createBy$', align: 'center',title: '娣诲姞浜哄憳'}
-            ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'updateBy$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
-            ,{field: 'systemStatus', align: 'center',title: '绯荤粺鐘舵�佹暟鎹�'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basRgvErrLog)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basRgvErrLog)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basRgvErrLog': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basRgvErrLog/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basRgvErrLog)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basRgvErrLog/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basRgvErrLog/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#startTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['startTime\\$']:null
-            });
-            layDate.render({
-                elem: '#endTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['endTime\\$']:null
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basRgvOpt/basRgvOpt.js b/src/main/webapp/static/js/basRgvOpt/basRgvOpt.js
index 05709e9..a88d35f 100644
--- a/src/main/webapp/static/js/basRgvOpt/basRgvOpt.js
+++ b/src/main/webapp/static/js/basRgvOpt/basRgvOpt.js
@@ -1,268 +1,1039 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basRgvOpt';
+    var entityName = 'BasRgvOpt';
+    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: 'taskNo',
+        columnName: 'task_no',
+        label: '宸� 浣� 鍙�',
+        tableProp: 'taskNo',
+        exportField: 'taskNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'rgvNo',
+        columnName: 'rgv_no',
+        label: 'RGV鍙�',
+        tableProp: 'rgvNo',
+        exportField: 'rgvNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'sendTime',
+        columnName: 'send_time',
+        label: '涓嬪彂鏃堕棿',
+        tableProp: 'sendTime$',
+        exportField: 'sendTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'mode',
+        columnName: 'mode',
+        label: '浣溿��銆�涓�',
+        tableProp: 'mode',
+        exportField: 'mode',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sourceLocNo',
+        columnName: 'source_loc_no',
+        label: '璧风偣搴撲綅',
+        tableProp: 'sourceLocNo',
+        exportField: 'sourceLocNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'targetLocNo',
+        columnName: 'target_loc_no',
+        label: '鐩爣搴撲綅',
+        tableProp: 'targetLocNo',
+        exportField: 'targetLocNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'command',
+        columnName: 'command',
+        label: '鍛姐��銆�浠�',
+        tableProp: 'command',
+        exportField: 'command',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'send',
+        columnName: 'send',
+        label: '涓嬪彂鐘舵��',
+        tableProp: 'send$',
+        exportField: 'send$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鏈笅鍙�' }, { rawValue: '1', label: '宸蹭笅鍙�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '璇锋眰鍝嶅簲',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basRgvOpt',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basRgvOpt/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'taskNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'rgvNo', align: 'center',title: 'RGV鍙�'}
-            ,{field: 'sendTime$', align: 'center',title: '涓嬪彂鏃堕棿'}
-            ,{field: 'mode', align: 'center',title: '浣滀笟'}
-            ,{field: 'sourceLocNo', align: 'center',title: '璧风偣搴撲綅'}
-            ,{field: 'targetLocNo', align: 'center',title: '鐩爣搴撲綅'}
-            ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'updateBy$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
-            ,{field: 'command', align: 'center',title: '鍛戒护'}
-            ,{field: 'systemStatus', align: 'center',title: '绯荤粺鐘舵��'}
-            ,{field: 'send$', align: 'center',title: '涓嬪彂鐘舵��'}
-            ,{field: 'response', align: 'center',title: '璇锋眰鍝嶅簲'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basRgvOpt)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basRgvOpt)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basRgvOpt': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basRgvOpt/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basRgvOpt)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basRgvOpt/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basRgvOpt/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#sendTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['sendTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basStation/basStation.js b/src/main/webapp/static/js/basStation/basStation.js
index 7404826..0ef3129 100644
--- a/src/main/webapp/static/js/basStation/basStation.js
+++ b/src/main/webapp/static/js/basStation/basStation.js
@@ -1,267 +1,1255 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basStation';
+    var entityName = 'BasStation';
+    var primaryKeyField = 'stationId';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'stationId',
+        columnName: 'station_id',
+        label: '缂栥��銆�鍙�',
+        tableProp: 'stationId',
+        exportField: 'stationId',
+        kind: 'text',
+        valueType: 'number',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        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: 'inEnable',
+        columnName: 'in_enable',
+        label: '鍙叆(checkBox)',
+        tableProp: 'inEnable',
+        exportField: 'inEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outEnable',
+        columnName: 'out_enable',
+        label: '鍙嚭(checkBox)',
+        tableProp: 'outEnable',
+        exportField: 'outEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'createBy',
+        columnName: 'create_by',
+        label: '鍒涘缓浜哄憳',
+        tableProp: 'createBy',
+        exportField: 'createBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'updateBy',
+        columnName: 'update_by',
+        label: '淇敼浜哄憳',
+        tableProp: 'updateBy',
+        exportField: 'updateBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: '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: 'stationLev',
+        columnName: 'station_lev',
+        label: '绔欑偣妤煎眰',
+        tableProp: 'stationLev',
+        exportField: 'stationLev',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deviceNo',
+        columnName: 'device_no',
+        label: '璁惧缂栧彿',
+        tableProp: 'deviceNo',
+        exportField: 'deviceNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'stationAlias',
+        columnName: 'station_alias',
+        label: '绔欑偣鍒悕',
+        tableProp: 'stationAlias',
+        exportField: 'stationAlias',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'stationId',
+        columnName: 'station_id',
+        label: '缂栥��銆�鍙�',
+        tableProp: 'stationId',
+        exportField: 'stationId',
+        kind: 'text',
+        valueType: 'number',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        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: 'inEnable',
+        columnName: 'in_enable',
+        label: '鍙叆(checkBox)',
+        tableProp: 'inEnable',
+        exportField: 'inEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'outEnable',
+        columnName: 'out_enable',
+        label: '鍙嚭(checkBox)',
+        tableProp: 'outEnable',
+        exportField: 'outEnable',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'createBy',
+        columnName: 'create_by',
+        label: '鍒涘缓浜哄憳',
+        tableProp: 'createBy',
+        exportField: 'createBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'updateBy',
+        columnName: 'update_by',
+        label: '淇敼浜哄憳',
+        tableProp: 'updateBy',
+        exportField: 'updateBy',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: '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: 'stationLev',
+        columnName: 'station_lev',
+        label: '绔欑偣妤煎眰',
+        tableProp: 'stationLev',
+        exportField: 'stationLev',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deviceNo',
+        columnName: 'device_no',
+        label: '璁惧缂栧彿',
+        tableProp: 'deviceNo',
+        exportField: 'deviceNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'stationAlias',
+        columnName: 'station_alias',
+        label: '绔欑偣鍒悕',
+        tableProp: 'stationAlias',
+        exportField: 'stationAlias',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basStation',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basStation/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'stationId', align: 'center',title: '缂栧彿'}
-            ,{field: 'status$', align: 'center',title: '鐘舵��'}
-            // ,{field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'inEnable', align: 'center',title: '鍙叆(checkBox)'}
-            ,{field: 'outEnable', align: 'center',title: '鍙嚭(checkBox)'}
-            // ,{field: 'createBy', align: 'center',title: '鍒涘缓浜哄憳'}
-            // ,{field: 'createTime$', align: 'center',title: '鍒涘缓鏃堕棿'}
-            // ,{field: 'updateBy', align: 'center',title: '淇敼浜哄憳'}
-            // ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
-            ,{field: 'stationLev', align: 'center',title: '绔欑偣妤煎眰'}
-            ,{field: 'deviceNo', align: 'center',title: '璁惧缂栧彿'}
-            ,{field: 'stationAlias', align: 'center',title: '绔欑偣鍒悕'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basStation)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basStation)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.stationId;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basStation': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basStation/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basStation)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.stationId]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basStation/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basStation/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basStationOpt/basStationOpt.js b/src/main/webapp/static/js/basStationOpt/basStationOpt.js
index 77ca7de..ac2270c 100644
--- a/src/main/webapp/static/js/basStationOpt/basStationOpt.js
+++ b/src/main/webapp/static/js/basStationOpt/basStationOpt.js
@@ -1,268 +1,1039 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'basStationOpt';
+    var entityName = 'BasStationOpt';
+    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: 'taskNo',
+        columnName: 'task_no',
+        label: '宸� 浣� 鍙�',
+        tableProp: 'taskNo',
+        exportField: 'taskNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'stationId',
+        columnName: 'station_id',
+        label: '绔欑偣缂栧彿',
+        tableProp: 'stationId',
+        exportField: 'stationId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'sendTime',
+        columnName: 'send_time',
+        label: '涓嬪彂鏃堕棿',
+        tableProp: 'sendTime$',
+        exportField: 'sendTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'mode',
+        columnName: 'mode',
+        label: '浣溿��銆�涓�',
+        tableProp: 'mode',
+        exportField: 'mode',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sourceStationId',
+        columnName: 'source_station_id',
+        label: '婧� 绔� 鐐�',
+        tableProp: 'sourceStationId',
+        exportField: 'sourceStationId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'targetStationId',
+        columnName: 'target_station_id',
+        label: '鐩爣绔欑偣',
+        tableProp: 'targetStationId',
+        exportField: 'targetStationId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: 'command',
+        columnName: 'command',
+        label: '鍛姐��銆�浠�',
+        tableProp: 'command',
+        exportField: 'command',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'send',
+        columnName: 'send',
+        label: '涓嬪彂鐘舵��',
+        tableProp: 'send$',
+        exportField: 'send$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鏈笅鍙�' }, { rawValue: '1', label: '宸蹭笅鍙�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '璇锋眰鍝嶅簲',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basStationOpt',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basStationOpt/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '缂栧彿'}
-            ,{field: 'taskNo', align: 'center',title: '宸ヤ綔鍙�'}
-            ,{field: 'stationId', align: 'center',title: '绔欑偣缂栧彿'}
-            ,{field: 'sendTime$', align: 'center',title: '涓嬪彂鏃堕棿'}
-            ,{field: 'mode', align: 'center',title: '浣滀笟'}
-            ,{field: 'sourceStationId', align: 'center',title: '婧愮珯鐐�'}
-            ,{field: 'targetStationId', align: 'center',title: '鐩爣绔欑偣'}
-            ,{field: 'updateTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{field: 'updateBy$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'memo', align: 'center',title: '澶囨敞'}
-            ,{field: 'command', align: 'center',title: '鍛戒护'}
-            ,{field: 'systemStatus', align: 'center',title: '绯荤粺鐘舵��'}
-            ,{field: 'send$', align: 'center',title: '涓嬪彂鐘舵��'}
-            ,{field: 'response', align: 'center',title: '璇锋眰鍝嶅簲'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basStationOpt)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basStationOpt)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basStationOpt': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basStationOpt/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basStationOpt)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/basStationOpt/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/basStationOpt/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#sendTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['sendTime\\$']:null
-            });
-            layDate.render({
-                elem: '#updateTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['updateTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/basWrkIotype/basWrkIotype.js b/src/main/webapp/static/js/basWrkIotype/basWrkIotype.js
index 723120b..815f549 100644
--- a/src/main/webapp/static/js/basWrkIotype/basWrkIotype.js
+++ b/src/main/webapp/static/js/basWrkIotype/basWrkIotype.js
@@ -1,443 +1,1165 @@
-var pageCurr;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
+(function () {
+    var simpleEntityName = 'basWrkIotype';
+    var entityName = 'BasWrkIotype';
+    var primaryKeyField = 'ioType';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'ioType',
+        columnName: 'io_type',
+        label: '鍏ュ嚭绫诲瀷浠e彿',
+        tableProp: 'ioType',
+        exportField: 'ioType',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioPri',
+        columnName: 'io_pri',
+        label: '涓昏',
+        tableProp: 'ioPri',
+        exportField: 'ioPri',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'ioDesc',
+        columnName: 'io_desc',
+        label: '鍏ュ嚭绫诲瀷鎻忚堪',
+        tableProp: 'ioDesc',
+        exportField: 'ioDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'ioType',
+        columnName: 'io_type',
+        label: '鍏ュ嚭绫诲瀷浠e彿',
+        tableProp: 'ioType',
+        exportField: 'ioType',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioPri',
+        columnName: 'io_pri',
+        label: '涓昏',
+        tableProp: 'ioPri',
+        exportField: 'ioPri',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'ioDesc',
+        columnName: 'io_desc',
+        label: '鍏ュ嚭绫诲瀷鎻忚堪',
+        tableProp: 'ioDesc',
+        exportField: 'ioDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'ioType',
+        columnName: 'io_type',
+        label: '鍏ュ嚭绫诲瀷浠e彿',
+        tableProp: 'ioType',
+        exportField: 'ioType',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioPri',
+        columnName: 'io_pri',
+        label: '涓昏',
+        tableProp: 'ioPri',
+        exportField: 'ioPri',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'ioDesc',
+        columnName: 'io_desc',
+        label: '鍏ュ嚭绫诲瀷鎻忚堪',
+        tableProp: 'ioDesc',
+        exportField: 'ioDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basWrkIotype',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basWrkIotype/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        // skin: 'line',
-        even: true,
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {type: 'checkbox', fixed: 'left'}
-//            ,{field: 'id', title: 'ID', sort: true,align: 'center', fixed: 'left', width: 80}
-            ,{field: 'ioType', align: 'center',sort:true,title: '鍏ュ嚭绫诲瀷浠e彿'}
-            // ,{field: 'ioPri', align: 'center',title: '涓昏'}
-            ,{field: 'ioDesc', align: 'center',title: '鍏ュ嚭绫诲瀷鎻忚堪'}
-            ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿'}
-            // ,{field: 'appeUser$', align: 'center',title: '鍒涘缓鑰�',event: 'appeUser', style: 'text-decoration: underline;cursor:pointer'}
-            // ,{field: 'appeTime$', align: 'center',title: '娣诲姞鏃堕棿'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:150}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basWrkIotype)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
+        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();
         });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
+    }
+
+    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;
+    }
 
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basWrkIotype)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: ['500px', top.detailHeight],
-                    shadeClose: false,
-                    content: 'basWrkIotype_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                if (data.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(data.length===1?'姝�':data.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/basWrkIotype/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {param: JSON.stringify(data)},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basWrkIotype': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basWrkIotype/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
+    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;
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basWrkIotype)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'basWrkIotype_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit-save,#prompt', index).hide();
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: ['500px', top.detailHeight],
-                    shadeClose: false,
-                    content: 'basWrkIotype_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-save', index).hide();
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        top.convertDisabled(layer.getChildFrame('#ioType', index), true);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            case 'modiUser':
-                var param = top.reObject(data).modiUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '淇敼璇︽儏',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#password,#createTime\\$,#status', index).parent().parent().hide();
-                                       layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-            case 'appeUser':
-                var param = top.reObject(data).appeUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '鍒涜鎯�',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-
+        if (rawValue === '') {
+            return '';
         }
-    });
-
-    // 鏁版嵁淇濆瓨鍔ㄤ綔
-    form.on('submit(save)', function () {
-        if (banMsg != null){
-            layer.msg(banMsg);
-            return;
+        if (field && field.valueType === 'number') {
+            var numberVal = Number(rawValue);
+            return isNaN(numberVal) ? rawValue : numberVal;
         }
-        method("add");
-    });
+        return String(rawValue);
+    }
 
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        method("update")
-    });
+    function isSearchableField(field) {
+        return !!field && field.kind !== 'image' && !field.textarea;
+    }
 
-    function method(name){
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-//            id: $('#id').val(),
-            ioType: $('#ioType').val(),
-            ioPri: $('#ioPri').val(),
-            ioDesc: $('#ioDesc').val(),
-            modiUser: $('#modiUser').val(),
-            modiTime: top.strToDate($('#modiTime\\$').val()),
-            appeUser: $('#appeUser').val(),
-            appeTime: top.strToDate($('#appeTime\\$').val()),
+    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: ''
         };
-        $.ajax({
-            url: baseUrl+"/basWrkIotype/"+name+"/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    tableReload(true);
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
-                }
-                layer.close(index);
+        fieldMeta.forEach(function (field) {
+            if (!isSearchableField(field)) {
+                return;
             }
-        })
+            result[field.field] = defaultSearchFieldValue(field);
+        });
+        return result;
     }
 
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
+    function createSearchDisplayDefaults() {
+        var result = {};
+        fieldMeta.forEach(function (field) {
+            if (field.kind === 'foreign' && isSearchableField(field)) {
+                result[field.field] = '';
+            }
+        });
+        return result;
+    }
 
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
+    function createDefaultVisibleColumnKeys() {
+        return fieldMeta.map(function (field) {
+            return field.field;
+        });
+    }
 
-    // 鏃堕棿閫夋嫨鍣�
-    layDate.render({
-        elem: '#modiTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#appeTime\\$',
-        type: 'datetime'
-    });
+    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;
+    }
 
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
+    function getTableValue(row, field) {
+        var prop = field.tableProp || field.field;
+        if (row && !isEmptyValue(row[prop])) {
+            return row[prop];
+        }
+        return row ? row[field.field] : '';
+    }
 
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
+    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);
+            };
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+        fetchForeignSuggestions: function (field, queryString, callback) {
+            if (!field.foreignQuery || !queryString) {
+                callback([]);
+                return;
             }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
-                    page: {
-                        curr: pageCurr-1
+            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;
                     }
-                });
-                pageCurr -= 1;
-            }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
+                    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, '');
         }
-    }
-}
+    };
 
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.9);
+    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('瀵煎嚭澶辫触');
+                        }
+                    });
+                }
+            })
+        });
     }
-    layer.style(index, {
-//        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
-    });
-}
 
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/basWrkStatus/basWrkStatus.js b/src/main/webapp/static/js/basWrkStatus/basWrkStatus.js
index a64f6da..c3b20f9 100644
--- a/src/main/webapp/static/js/basWrkStatus/basWrkStatus.js
+++ b/src/main/webapp/static/js/basWrkStatus/basWrkStatus.js
@@ -1,441 +1,1111 @@
-var pageCurr;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
+(function () {
+    var simpleEntityName = 'basWrkStatus';
+    var entityName = 'BasWrkStatus';
+    var primaryKeyField = 'wrkSts';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'wrkSts',
+        columnName: 'wrk_sts',
+        label: '浠e彿',
+        tableProp: 'wrkSts',
+        exportField: 'wrkSts',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wrkDesc',
+        columnName: 'wrk_desc',
+        label: '鐘舵�佹弿杩�',
+        tableProp: 'wrkDesc',
+        exportField: 'wrkDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        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: '浠e彿',
+        tableProp: 'wrkSts',
+        exportField: 'wrkSts',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wrkDesc',
+        columnName: 'wrk_desc',
+        label: '鐘舵�佹弿杩�',
+        tableProp: 'wrkDesc',
+        exportField: 'wrkDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        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: '浠e彿',
+        tableProp: 'wrkSts',
+        exportField: 'wrkSts',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wrkDesc',
+        columnName: 'wrk_desc',
+        label: '鐘舵�佹弿杩�',
+        tableProp: 'wrkDesc',
+        exportField: 'wrkDesc',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#basWrkStatus',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/basWrkStatus/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        // skin: 'line',
-        even: true,
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {type: 'checkbox', fixed: 'left'}
-//            ,{field: 'id', title: 'ID', sort: true,align: 'center', fixed: 'left', width: 80}
-            ,{field: 'wrkSts', align: 'center',sort:true,title: '浠e彿'}
-            ,{field: 'wrkDesc', align: 'center',title: '鐘舵�佹弿杩�'}
-            ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿'}
-            // ,{field: 'appeUser$', align: 'center',title: '鍒涘缓鑰�',event: 'appeUser', style: 'text-decoration: underline;cursor:pointer'}
-            // ,{field: 'appeTime$', align: 'center',title: '娣诲姞鏃堕棿'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:150}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(basWrkStatus)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
+        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();
         });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
+    }
+
+    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;
+    }
 
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(basWrkStatus)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: ['500px', top.detailHeight],
-                    shadeClose: false,
-                    content: 'basWrkStatus_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                if (data.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(data.length===1?'姝�':data.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/basWrkStatus/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {param: JSON.stringify(data)},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'basWrkStatus': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/basWrkStatus/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
+    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;
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(basWrkStatus)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'basWrkStatus_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit-save,#prompt', index).hide();
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: ['500px', top.detailHeight],
-                    shadeClose: false,
-                    content: 'basWrkStatus_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-save', index).hide();
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        top.convertDisabled(layer.getChildFrame('#wrkSts', index), true);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            case 'modiUser':
-                var param = top.reObject(data).modiUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '淇敼璇︽儏',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                                       layer.getChildFrame('#password,#createTime\\$,#status', index).parent().parent().hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-            case 'appeUser':
-                var param = top.reObject(data).appeUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '鍒涜鎯�',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-
+        if (rawValue === '') {
+            return '';
         }
-    });
-
-    // 鏁版嵁淇濆瓨鍔ㄤ綔
-    form.on('submit(save)', function () {
-        if (banMsg != null){
-            layer.msg(banMsg);
-            return;
+        if (field && field.valueType === 'number') {
+            var numberVal = Number(rawValue);
+            return isNaN(numberVal) ? rawValue : numberVal;
         }
-        method("add");
-    });
+        return String(rawValue);
+    }
 
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        method("update")
-    });
+    function isSearchableField(field) {
+        return !!field && field.kind !== 'image' && !field.textarea;
+    }
 
-    function method(name){
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-//            id: $('#id').val(),
-            wrkSts: $('#wrkSts').val(),
-            wrkDesc: $('#wrkDesc').val(),
-            modiUser: $('#modiUser').val(),
-            modiTime: top.strToDate($('#modiTime\\$').val()),
-            appeUser: $('#appeUser').val(),
-            appeTime: top.strToDate($('#appeTime\\$').val()),
+    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: ''
         };
-        $.ajax({
-            url: baseUrl+"/basWrkStatus/"+name+"/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    tableReload(true);
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
-                }
-                layer.close(index);
+        fieldMeta.forEach(function (field) {
+            if (!isSearchableField(field)) {
+                return;
             }
-        })
+            result[field.field] = defaultSearchFieldValue(field);
+        });
+        return result;
     }
 
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
+    function createSearchDisplayDefaults() {
+        var result = {};
+        fieldMeta.forEach(function (field) {
+            if (field.kind === 'foreign' && isSearchableField(field)) {
+                result[field.field] = '';
+            }
+        });
+        return result;
+    }
 
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
+    function createDefaultVisibleColumnKeys() {
+        return fieldMeta.map(function (field) {
+            return field.field;
+        });
+    }
 
-    // 鏃堕棿閫夋嫨鍣�
-    layDate.render({
-        elem: '#modiTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#appeTime\\$',
-        type: 'datetime'
-    });
+    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;
+    }
 
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
+    function getTableValue(row, field) {
+        var prop = field.tableProp || field.field;
+        if (row && !isEmptyValue(row[prop])) {
+            return row[prop];
+        }
+        return row ? row[field.field] : '';
+    }
 
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
+    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);
+            };
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+        fetchForeignSuggestions: function (field, queryString, callback) {
+            if (!field.foreignQuery || !queryString) {
+                callback([]);
+                return;
             }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
-                    page: {
-                        curr: pageCurr-1
+            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;
                     }
-                });
-                pageCurr -= 1;
-            }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
+                    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, '');
         }
-    }
-}
+    };
 
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.9);
+    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('瀵煎嚭澶辫触');
+                        }
+                    });
+                }
+            })
+        });
     }
-    layer.style(index, {
-//        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
-    });
-}
 
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/config/config.js b/src/main/webapp/static/js/config/config.js
index 2be0919..483f5c6 100644
--- a/src/main/webapp/static/js/config/config.js
+++ b/src/main/webapp/static/js/config/config.js
@@ -1,382 +1,1597 @@
-var pageCurr;
-layui.use(['table', 'laydate', 'form'], function () {
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
+(function () {
+    var simpleEntityName = 'config';
+    var entityName = 'Config';
+    var primaryKeyField = 'id';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'name',
+        columnName: 'name',
+        label: '鍚嶇О',
+        tableProp: 'name',
+        exportField: 'name',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'code',
+        columnName: 'code',
+        label: '缂栫爜',
+        tableProp: 'code',
+        exportField: 'code',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'value',
+        columnName: 'value',
+        label: '瀵瑰簲鍊�',
+        tableProp: 'value',
+        exportField: 'value',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'type',
+        columnName: 'type',
+        label: '绫诲瀷',
+        tableProp: 'type',
+        exportField: 'type',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'name',
+        columnName: 'name',
+        label: '鍚嶇О',
+        tableProp: 'name',
+        exportField: 'name',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'code',
+        columnName: 'code',
+        label: '缂栫爜',
+        tableProp: 'code',
+        exportField: 'code',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'value',
+        columnName: 'value',
+        label: '瀵瑰簲鍊�',
+        tableProp: 'value',
+        exportField: 'value',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'type',
+        columnName: 'type',
+        label: '绫诲瀷',
+        tableProp: 'type',
+        exportField: 'type',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'selectType',
+        columnName: 'select_type',
+        label: '绛涢�夌被鍨�',
+        tableProp: 'selectType',
+        exportField: 'selectType',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'id',
+        columnName: 'id',
+        label: 'I銆�銆�D',
+        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: 'uuid',
+        columnName: 'uuid',
+        label: '缂栥��銆�鍙�',
+        tableProp: 'uuid',
+        exportField: 'uuid',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'name',
+        columnName: 'name',
+        label: '鍚嶃��銆�绉�',
+        tableProp: 'name',
+        exportField: 'name',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'flag',
+        columnName: 'flag',
+        label: '鏍囥��銆�璇�',
+        tableProp: 'flag',
+        exportField: 'flag',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'type',
+        columnName: 'type',
+        label: '绫汇��銆�鍨�',
+        tableProp: 'type$',
+        exportField: 'type$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '1', label: 'boolean' }, { rawValue: '2', label: 'number' }, { rawValue: '3', label: 'string' }, { rawValue: '4', label: 'json' }, { rawValue: '5', label: 'date' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'val',
+        columnName: 'val',
+        label: '瀛� 鍏� 鍊�',
+        tableProp: 'val',
+        exportField: 'val',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'content',
+        columnName: 'content',
+        label: '瀛楀吀鏂囨湰',
+        tableProp: 'content',
+        exportField: 'content',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '鐘躲��銆�鎬�',
+        tableProp: 'status$',
+        exportField: 'status$',
+        kind: 'enum',
+        valueType: 'number',
+        required: true,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '1', label: '姝e父' }, { rawValue: '0', label: '鍐荤粨' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deleted',
+        columnName: 'deleted',
+        label: '鏄惁鍒犻櫎',
+        tableProp: 'deleted$',
+        exportField: 'deleted$',
+        kind: 'enum',
+        valueType: 'number',
+        required: true,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '1', label: '鏄�' }, { rawValue: '0', label: '鍚�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'tenantId',
+        columnName: 'tenant_id',
+        label: '绉熴��銆�鎴�',
+        tableProp: 'tenantId$',
+        exportField: 'tenantId$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'tenant',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: 'createTime',
+        columnName: 'create_time',
+        label: '娣诲姞鏃堕棿',
+        tableProp: 'createTime$',
+        exportField: 'createTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: true,
+        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: '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: '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: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'name',
+        columnName: 'name',
+        label: '鍚嶇О',
+        tableProp: 'name',
+        exportField: 'name',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'code',
+        columnName: 'code',
+        label: '缂栫爜',
+        tableProp: 'code',
+        exportField: 'code',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'value',
+        columnName: 'value',
+        label: '瀵瑰簲鍊�',
+        tableProp: 'value',
+        exportField: 'value',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'type',
+        columnName: 'type',
+        label: '绫诲瀷',
+        tableProp: 'type',
+        exportField: 'type',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'variable',
+        columnName: 'variable',
+        label: '',
+        tableProp: 'variable',
+        exportField: 'variable',
+        kind: 'text',
+        valueType: 'string',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'value',
+        columnName: 'value',
+        label: '瀵瑰簲鍊�',
+        tableProp: 'value',
+        exportField: 'value',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'setTime',
+        columnName: 'set_time',
+        label: '',
+        tableProp: 'setTime$',
+        exportField: 'setTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: true,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'setBy',
+        columnName: 'set_by',
+        label: '',
+        tableProp: 'setBy',
+        exportField: 'setBy',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'name',
+        columnName: 'name',
+        label: '鍚嶇О',
+        tableProp: 'name',
+        exportField: 'name',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'code',
+        columnName: 'code',
+        label: '缂栫爜',
+        tableProp: 'code',
+        exportField: 'code',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'value',
+        columnName: 'value',
+        label: '瀵瑰簲鍊�',
+        tableProp: 'value',
+        exportField: 'value',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'type',
+        columnName: 'type',
+        label: '绫诲瀷',
+        tableProp: 'type',
+        exportField: 'type',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'selectType',
+        columnName: 'select_type',
+        label: '绛涢�夌被鍨�',
+        tableProp: 'selectType',
+        exportField: 'selectType',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#config',
-        headers: { token: localStorage.getItem('token') },
-        url: baseUrl + '/config/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            { type: 'checkbox', fixed: 'left' }
-            , { field: 'id', title: 'ID', sort: true, align: 'center', fixed: 'left', width: 80 }
-            , { field: 'name', align: 'center', title: '鍚嶇О' }
-            , { field: 'code', align: 'center', title: '缂栫爜' }
-            , { field: 'value', align: 'center', title: '瀵瑰簲鍊�' }
-            , { field: 'selectType', align: 'center', title: '绛涢�夌被鍨�' }
-            , { field: 'type$', align: 'center', title: '绫诲瀷' }
-            , { field: 'status$', align: 'center', title: '鐘舵��' }
+    ]);
 
-            , { fixed: 'right', title: '鎿嶄綔', align: 'center', toolbar: '#operate', width: 150 }
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl + "/";
-            }
-            pageCurr = curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(config)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function () {
-            searchData[this.name] = this.value;
+        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();
         });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl + "/";
-                }
-                pageCurr = curr;
-                limit();
+    }
+
+    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;
+    }
 
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(config)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch (obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'config_detail.html',
-                    success: function (layero, index) {
-                        clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index); layer.style(index, { top: (($(window).height() - layer.getChildFrame('#data-detail', index).height()) / 3) + "px" });
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                var ids = [];
-                data.map(function (track) {
-                    ids.push(track.id);
-                });
-                if (ids.length === 0) {
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎' + (ids.length === 1 ? '姝�' : ids.length) + '鏉℃暟鎹悧', function () {
-                        $.ajax({
-                            url: baseUrl + "/config/delete/auth",
-                            headers: { 'token': localStorage.getItem('token') },
-                            data: { ids: ids },
-                            method: 'POST',
-                            traditional: true,
-                            success: function (res) {
-                                if (res.code === 200) {
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403) {
-                                    top.location.href = baseUrl + "/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'refreshCache':
-                layer.confirm('纭畾鍒锋柊Redis缂撳瓨鍚�', function () {
-                    $.ajax({
-                        url: baseUrl + "/config/refreshCache",
-                        headers: { 'token': localStorage.getItem('token') },
-                        method: 'POST',
-                        success: function (res) {
-                            if (res.code === 200) {
-                                layer.msg('鍒锋柊鎴愬姛');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl + "/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    })
-                });
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', { shadeClose: true }, function () {
-                    var titles = [];
-                    var fields = [];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function () {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'config': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl + "/config/export/auth",
-                        headers: { 'token': localStorage.getItem('token') },
-                        data: JSON.stringify(param),
-                        dataType: 'json',
-                        contentType: 'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles, res.data, 'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl + "/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
+    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;
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(config)', function (obj) {
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'config_detail.html',
-                    success: function (layero, index) {
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                        layer.iframeAuto(index); layer.style(index, { top: (($(window).height() - layer.getChildFrame('#data-detail', index).height()) / 3) + "px" });
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'config_detail.html',
-                    success: function (layero, index) {
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        layer.iframeAuto(index); layer.style(index, { top: (($(window).height() - layer.getChildFrame('#data-detail', index).height()) / 3) + "px" });
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-
+        if (rawValue === '') {
+            return '';
         }
-    });
+        if (field && field.valueType === 'number') {
+            var numberVal = Number(rawValue);
+            return isNaN(numberVal) ? rawValue : numberVal;
+        }
+        return String(rawValue);
+    }
 
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        var index = layer.load(1, {
-            shade: [0.5, '#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-            id: $('#id').val(),
-            name: $('#name').val(),
-            code: $('#code').val(),
-            value: $('#value').val(),
-            selectType: $('#selectType').val(),
-            type: $('#type').val(),
-            status: $('#status').val(),
+    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: ''
         };
-        $.ajax({
-            url: baseUrl + "/config/edit/auth",
-            headers: { 'token': localStorage.getItem('token') },
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200) {
-                    parent.layer.closeAll();
-                    tableReload(true);
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403) {
-                    top.location.href = baseUrl + "/";
-                } else {
-                    layer.msg(res.msg)
-                }
-                layer.close(index);
+        fieldMeta.forEach(function (field) {
+            if (!isSearchableField(field)) {
+                return;
             }
-        })
-    });
+            result[field.field] = defaultSearchFieldValue(field);
+        });
+        return result;
+    }
 
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-
-    // 鍒濆鍖栫瓫閫夌被鍨嬩笅鎷夋
-    $.ajax({
-        url: baseUrl + "/config/getSelectTypes",
-        headers: { 'token': localStorage.getItem('token') },
-        method: 'POST',
-        success: function (res) {
-            if (res.code === 200) {
-                var types = res.data;
-                var select = $("#selectTypeSearch");
-                for (var i = 0; i < types.length; i++) {
-                    select.append("<option value='" + types[i] + "'>" + types[i] + "</option>");
-                }
-                form.render('select');
+    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);
+    }
 
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click', '#data-detail-close', function () {
-    parent.layer.closeAll();
-});
+    function exportCell(value) {
+        return stringValue(value).replace(/\t/g, ' ').replace(/\r?\n/g, ' ');
+    }
 
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function () {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
+    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);
+            };
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl + "/";
+        fetchForeignSuggestions: function (field, queryString, callback) {
+            if (!field.foreignQuery || !queryString) {
+                callback([]);
+                return;
             }
-            pageCurr = curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
-                    page: {
-                        curr: pageCurr - 1
+            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;
                     }
-                });
-                pageCurr -= 1;
-            }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        find.val(data[val]);
-        if (showImg) {
-            var next = find.next();
-            if (next.get(0)) {
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
+                    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, '');
         }
-    }
-}
+    };
 
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height() + 60;
-    if (height > ($(window).height() * 0.9)) {
-        height = ($(window).height() * 0.9);
+    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('瀵煎嚭澶辫触');
+                        }
+                    });
+                }
+            })
+        });
     }
-    layer.style(index, {
-        top: (($(window).height() - height) / 3) + "px",
-        height: height + 'px'
-    });
-    $(".layui-layer-shade").remove();
-}
 
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/detail/detail.js b/src/main/webapp/static/js/detail/detail.js
new file mode 100644
index 0000000..2c4c7d5
--- /dev/null
+++ b/src/main/webapp/static/js/detail/detail.js
@@ -0,0 +1,247 @@
+(function () {
+    function handleForbidden(res) {
+        if (res && Number(res.code) === 403) {
+            top.location.href = baseUrl + "/";
+            return true;
+        }
+        return false;
+    }
+
+    new Vue({
+        el: "#app",
+        data: function () {
+            return {
+                loading: false,
+                saving: false,
+                passwordDialogVisible: false,
+                passwordSaving: false,
+                form: {
+                    id: "",
+                    roleName: "",
+                    username: "",
+                    mobile: "",
+                    password: "",
+                    createTime$: ""
+                },
+                passwordForm: {
+                    oldPassword: "",
+                    password: "",
+                    rePassword: ""
+                },
+                rules: {
+                    username: [
+                        { required: true, message: "璇疯緭鍏ュ悕绉�", trigger: "blur" }
+                    ],
+                    mobile: [
+                        { required: true, message: "璇疯緭鍏ヨ处鍙�", trigger: "blur" }
+                    ]
+                },
+                passwordRules: {
+                    oldPassword: [
+                        { required: true, message: "璇疯緭鍏ュ綋鍓嶅瘑鐮�", trigger: "blur" },
+                        {
+                            validator: function (rule, value, callback) {
+                                if (!value) {
+                                    callback(new Error("璇疯緭鍏ュ綋鍓嶅瘑鐮�"));
+                                    return;
+                                }
+                                if (!this.form.password) {
+                                    callback(new Error("鏈鍙栧埌褰撳墠鐢ㄦ埛瀵嗙爜"));
+                                    return;
+                                }
+                                if (hex_md5(value) !== this.form.password) {
+                                    callback(new Error("瀵嗙爜涓嶅尮閰�"));
+                                    return;
+                                }
+                                callback();
+                            }.bind(this),
+                            trigger: "blur"
+                        }
+                    ],
+                    password: [
+                        { required: true, message: "璇疯緭鍏ユ柊瀵嗙爜", trigger: "blur" },
+                        {
+                            validator: function (rule, value, callback) {
+                                if (!value) {
+                                    callback(new Error("璇疯緭鍏ユ柊瀵嗙爜"));
+                                    return;
+                                }
+                                if (String(value).length < 4) {
+                                    callback(new Error("涓嶈兘灏戜簬4涓瓧绗�"));
+                                    return;
+                                }
+                                if (this.form.password && hex_md5(value) === this.form.password) {
+                                    callback(new Error("涓庢棫瀵嗙爜涓嶈兘鐩稿悓"));
+                                    return;
+                                }
+                                callback();
+                            }.bind(this),
+                            trigger: "blur"
+                        }
+                    ],
+                    rePassword: [
+                        { required: true, message: "璇峰啀娆¤緭鍏ユ柊瀵嗙爜", trigger: "blur" },
+                        {
+                            validator: function (rule, value, callback) {
+                                if (value !== this.passwordForm.password) {
+                                    callback(new Error("瀵嗙爜涓嶄竴鑷�"));
+                                    return;
+                                }
+                                callback();
+                            }.bind(this),
+                            trigger: "blur"
+                        }
+                    ]
+                }
+            };
+        },
+        created: function () {
+            this.loadDetail();
+        },
+        methods: {
+            loadDetail: function () {
+                var vm = this;
+                vm.loading = true;
+                $.ajax({
+                    url: baseUrl + "/user/detail/auth",
+                    headers: { token: localStorage.getItem("token") },
+                    method: "POST",
+                    success: function (res) {
+                        if (handleForbidden(res)) {
+                            return;
+                        }
+                        if (Number(res.code) !== 200) {
+                            vm.$message.error(res.msg || "璧勬枡鍔犺浇澶辫触");
+                            return;
+                        }
+                        var user = res.data || {};
+                        vm.form = Object.assign({}, vm.form, user);
+                    },
+                    error: function () {
+                        vm.$message.error("璧勬枡鍔犺浇澶辫触");
+                    },
+                    complete: function () {
+                        vm.loading = false;
+                    }
+                });
+            },
+            openPasswordDialog: function () {
+                this.passwordForm = {
+                    oldPassword: "",
+                    password: "",
+                    rePassword: ""
+                };
+                this.passwordDialogVisible = true;
+                if (this.$refs.passwordForm) {
+                    this.$nextTick(function () {
+                        this.$refs.passwordForm.clearValidate();
+                    });
+                }
+            },
+            closePasswordDialog: function () {
+                this.passwordDialogVisible = false;
+            },
+            handlePasswordSave: function () {
+                var vm = this;
+                vm.$refs.passwordForm.validate(function (valid) {
+                    if (!valid) {
+                        return false;
+                    }
+                    vm.submitPasswordSave();
+                    return true;
+                });
+            },
+            submitPasswordSave: function () {
+                var vm = this;
+                vm.passwordSaving = true;
+                $.ajax({
+                    url: baseUrl + "/user/update/auth",
+                    headers: { token: localStorage.getItem("token") },
+                    data: {
+                        id: vm.form.id,
+                        password: hex_md5(vm.passwordForm.password)
+                    },
+                    method: "POST",
+                    success: function (res) {
+                        if (handleForbidden(res)) {
+                            return;
+                        }
+                        if (Number(res.code) !== 200) {
+                            vm.$message.error(res.msg || "瀵嗙爜淇敼澶辫触");
+                            return;
+                        }
+                        vm.form.password = hex_md5(vm.passwordForm.password);
+                        vm.passwordDialogVisible = false;
+                        vm.$alert("瀵嗙爜淇敼鎴愬姛锛岃閲嶆柊鐧诲綍", "鎻愮ず", {
+                            confirmButtonText: "纭畾",
+                            callback: function () {
+                                localStorage.removeItem("token");
+                                top.location.href = baseUrl + "/";
+                            }
+                        });
+                    },
+                    error: function () {
+                        vm.$message.error("瀵嗙爜淇敼澶辫触");
+                    },
+                    complete: function () {
+                        vm.passwordSaving = false;
+                    }
+                });
+            },
+            handleSave: function () {
+                var vm = this;
+                vm.$refs.profileForm.validate(function (valid) {
+                    if (!valid) {
+                        return false;
+                    }
+                    vm.$confirm("纭畾淇敼璧勬枡鍚楋紵", "鎻愮ず", {
+                        type: "warning",
+                        confirmButtonText: "纭畾",
+                        cancelButtonText: "鍙栨秷"
+                    }).then(function () {
+                        vm.submitSave();
+                    }).catch(function () {
+                    });
+                    return true;
+                });
+            },
+            submitSave: function () {
+                var vm = this;
+                vm.saving = true;
+                $.ajax({
+                    url: baseUrl + "/user/update/auth",
+                    headers: { token: localStorage.getItem("token") },
+                    data: {
+                        id: vm.form.id,
+                        username: vm.form.username,
+                        mobile: vm.form.mobile
+                    },
+                    method: "POST",
+                    success: function (res) {
+                        if (handleForbidden(res)) {
+                            return;
+                        }
+                        if (Number(res.code) !== 200) {
+                            vm.$message.error(res.msg || "淇濆瓨澶辫触");
+                            return;
+                        }
+                        vm.$message.success(res.msg || "淇敼鎴愬姛");
+                        localStorage.setItem("username", vm.form.username || "");
+                        if (window.parent && window.parent.document) {
+                            var usernameNode = window.parent.document.getElementById("person-username");
+                            if (usernameNode) {
+                                usernameNode.textContent = localStorage.getItem("username");
+                            }
+                        }
+                    },
+                    error: function () {
+                        vm.$message.error("淇濆瓨澶辫触");
+                    },
+                    complete: function () {
+                        vm.saving = false;
+                    }
+                });
+            }
+        }
+    });
+})();
diff --git a/src/main/webapp/static/js/deviceConfig/deviceConfig.js b/src/main/webapp/static/js/deviceConfig/deviceConfig.js
index bc11605..756482f 100644
--- a/src/main/webapp/static/js/deviceConfig/deviceConfig.js
+++ b/src/main/webapp/static/js/deviceConfig/deviceConfig.js
@@ -1,258 +1,1147 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'deviceConfig';
+    var entityName = 'DeviceConfig';
+    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: 'ip',
+        columnName: 'ip',
+        label: '璁惧ip',
+        tableProp: 'ip',
+        exportField: 'ip',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'port',
+        columnName: 'port',
+        label: '璁惧绔彛',
+        tableProp: 'port',
+        exportField: 'port',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'threadImpl',
+        columnName: 'thread_impl',
+        label: '瀹� 鐜� 绫�',
+        tableProp: 'threadImpl',
+        exportField: 'threadImpl',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'deviceType',
+        columnName: 'device_type',
+        label: '璁惧绫诲瀷',
+        tableProp: 'deviceType',
+        exportField: 'deviceType',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'deviceNo',
+        columnName: 'device_no',
+        label: '璁惧缂栧彿',
+        tableProp: 'deviceNo',
+        exportField: 'deviceNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'fake',
+        columnName: 'fake',
+        label: '铏氭嫙璁惧',
+        tableProp: 'fake$',
+        exportField: 'fake$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鍚�' }, { rawValue: '1', label: '鏄�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'fakeInitStatus',
+        columnName: 'fake_init_status',
+        label: '铏氭嫙璁惧鍒濆鍖栫姸鎬�',
+        tableProp: 'fakeInitStatus',
+        exportField: 'fakeInitStatus',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'gatewayId',
+        columnName: 'gateway_id',
+        label: '缃戝叧缂栧彿',
+        tableProp: 'gatewayId',
+        exportField: 'gatewayId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: 'ip',
+        columnName: 'ip',
+        label: '璁惧ip',
+        tableProp: 'ip',
+        exportField: 'ip',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'port',
+        columnName: 'port',
+        label: '璁惧绔彛',
+        tableProp: 'port',
+        exportField: 'port',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'threadImpl',
+        columnName: 'thread_impl',
+        label: '瀹� 鐜� 绫�',
+        tableProp: 'threadImpl',
+        exportField: 'threadImpl',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'deviceType',
+        columnName: 'device_type',
+        label: '璁惧绫诲瀷',
+        tableProp: 'deviceType',
+        exportField: 'deviceType',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'deviceNo',
+        columnName: 'device_no',
+        label: '璁惧缂栧彿',
+        tableProp: 'deviceNo',
+        exportField: 'deviceNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'fake',
+        columnName: 'fake',
+        label: '铏氭嫙璁惧',
+        tableProp: 'fake$',
+        exportField: 'fake$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鍚�' }, { rawValue: '1', label: '鏄�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'fakeInitStatus',
+        columnName: 'fake_init_status',
+        label: '铏氭嫙璁惧鍒濆鍖栫姸鎬�',
+        tableProp: 'fakeInitStatus',
+        exportField: 'fakeInitStatus',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'gatewayId',
+        columnName: 'gateway_id',
+        label: '缃戝叧缂栧彿',
+        tableProp: 'gatewayId',
+        exportField: 'gatewayId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#deviceConfig',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/deviceConfig/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'deviceNo', align: 'center',title: '璁惧缂栧彿'}
-            ,{field: 'deviceType', align: 'center',title: '璁惧绫诲瀷'}
-            ,{field: 'ip', align: 'center',title: '璁惧ip'}
-            ,{field: 'port', align: 'center',title: '璁惧绔彛'}
-            ,{field: 'threadImpl', align: 'center',title: '瀹炵幇绫�'}
-            ,{field: 'gatewayId', align: 'center',title: '缃戝叧缂栧彿'}
-            ,{field: 'fake$', align: 'center',title: '铏氭嫙璁惧'}
-            ,{field: 'fakeInitStatus', align: 'center',title: '铏氭嫙璁惧鍒濆鍖栬澶囩姸鎬�'}
-            ,{field: 'createTime$', align: 'center',title: '鍒涘缓鏃堕棿'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(deviceConfig)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(deviceConfig)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'deviceConfig': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/deviceConfig/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(deviceConfig)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/deviceConfig/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/deviceConfig/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/httpRequestLog/httpRequestLog.js b/src/main/webapp/static/js/httpRequestLog/httpRequestLog.js
index da9ed91..6f58332 100644
--- a/src/main/webapp/static/js/httpRequestLog/httpRequestLog.js
+++ b/src/main/webapp/static/js/httpRequestLog/httpRequestLog.js
@@ -1,255 +1,1147 @@
-var pageCurr;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
+(function () {
+    var simpleEntityName = 'httpRequestLog';
+    var entityName = 'HttpRequestLog';
+    var primaryKeyField = 'id';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'name',
+        columnName: 'name',
+        label: 'URL',
+        tableProp: 'name',
+        exportField: 'name',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鍙傛暟',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鍙傛暟',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'id',
+        columnName: 'id',
+        label: '#ID',
+        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: 'name',
+        columnName: 'name',
+        label: 'URL',
+        tableProp: 'name',
+        exportField: 'name',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鍙傛暟',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鍙傛暟',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'result',
+        columnName: 'result',
+        label: '缁撴灉',
+        tableProp: 'result',
+        exportField: 'result',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'name',
+        columnName: 'name',
+        label: 'URL',
+        tableProp: 'name',
+        exportField: 'name',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鍙傛暟',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鍙傛暟',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'id',
+        columnName: 'id',
+        label: '#ID',
+        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: 'name',
+        columnName: 'name',
+        label: 'URL',
+        tableProp: 'name',
+        exportField: 'name',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鍙傛暟',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鍙傛暟',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'result',
+        columnName: 'result',
+        label: '缁撴灉',
+        tableProp: 'result',
+        exportField: 'result',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#httpRequestLog',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/httpRequestLog/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        height: 'full-120',
-        cols: [[
-            {type: 'checkbox'}
-            ,{field: 'id', align: 'center',title: '#ID'}
-            ,{field: 'name', align: 'center',title: 'URL'}
-            ,{field: 'request', align: 'center',title: '璇锋眰鍙傛暟'}
-            ,{field: 'response', align: 'center',title: '鍝嶅簲鍙傛暟'}
-            ,{field: 'result$', align: 'center',title: '缁撴灉', templet: '#resTpl', width: 80}
-            ,{field: 'createTime$', align: 'center',title: '璇锋眰鏃堕棿'}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:120}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(httpRequestLog)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {curr: 1}
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(httpRequestLog)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        switch(obj.event) {
-            case 'addData':
-                showEditModel();
-                break;
-            case 'deleteData':
-               if (checkStatus.length === 0) {
-                   layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                   return;
-               }
-               del(checkStatus.map(function (d) {
-                   return d.id;
-               }));
-               break;
-            case 'exportData':
-                admin.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'httpRequestLog': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/httpRequestLog/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg, {icon: 2})
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(httpRequestLog)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            case 'edit':
-                showEditModel(data);
-                break;
-            case "del":
-                del([data.id]);
-                break;
-        }
-    });
-
-    /* 寮圭獥 - 鏂板銆佷慨鏀� */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '璁㈠崟鐘舵��',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                layDateRender(mData);
-                form.val('detail', mData);
-                form.on('submit(editSubmit)', function (data) {
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/httpRequestLog/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                tableReload();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
-            }
+        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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/httpRequestLog/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        tableReload();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
         });
     }
 
-    // 鎼滅储
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 閲嶇疆
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    function layDateRender(data) {
-        setTimeout(function () {
-            layDate.render({
-                elem: '.layui-laydate-range'
-                ,type: 'datetime'
-                ,range: true
-            });
-            layDate.render({
-                elem: '#createTime\\$',
-                type: 'datetime',
-                value: data!==undefined?data['createTime\\$']:null
-            });
-
-        }, 300);
-    }
-    layDateRender();
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    tableIns.reload({
-        where: searchData,
-        page: {curr: pageCurr}
-     });
-}
+})();
diff --git a/src/main/webapp/static/js/locMast/locMast.js b/src/main/webapp/static/js/locMast/locMast.js
index 3900990..fa699f5 100644
--- a/src/main/webapp/static/js/locMast/locMast.js
+++ b/src/main/webapp/static/js/locMast/locMast.js
@@ -1,554 +1,1777 @@
-var pageCurr;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
+(function () {
+    var simpleEntityName = 'locMast';
+    var entityName = 'LocMast';
+    var primaryKeyField = 'locNo';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'locNo',
+        columnName: 'loc_no',
+        label: '搴撲綅鍙�',
+        tableProp: 'locNo',
+        exportField: 'locNo',
+        kind: 'text',
+        valueType: 'string',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locSts',
+        columnName: 'loc_sts',
+        label: '搴撲綅鐘舵��',
+        tableProp: 'locSts',
+        exportField: 'locSts',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'row1',
+        columnName: 'row1',
+        label: '鎺�',
+        tableProp: 'row1',
+        exportField: 'row1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'bay1',
+        columnName: 'bay1',
+        label: '鍒�',
+        tableProp: 'bay1',
+        exportField: 'bay1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'lev1',
+        columnName: 'lev1',
+        label: '灞�',
+        tableProp: 'lev1',
+        exportField: 'lev1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType',
+        columnName: 'loc_type',
+        label: '搴撲綅绫诲瀷',
+        tableProp: 'locType',
+        exportField: 'locType',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locType1',
+        columnName: 'loc_type1',
+        label: '',
+        tableProp: 'locType1',
+        exportField: 'locType1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType2',
+        columnName: 'loc_type2',
+        label: '',
+        tableProp: 'locType2',
+        exportField: 'locType2',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType3',
+        columnName: 'loc_type3',
+        label: '',
+        tableProp: 'locType3',
+        exportField: 'locType3',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioTime',
+        columnName: 'io_time',
+        label: '',
+        tableProp: 'ioTime$',
+        exportField: 'ioTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorTime',
+        columnName: 'error_time',
+        label: '',
+        tableProp: 'errorTime$',
+        exportField: 'errorTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorMemo',
+        columnName: 'error_memo',
+        label: '',
+        tableProp: 'errorMemo',
+        exportField: 'errorMemo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'qrCodeValue',
+        columnName: 'qr_code_value',
+        label: '',
+        tableProp: 'qrCodeValue',
+        exportField: 'qrCodeValue',
+        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: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locNo',
+        columnName: 'loc_no',
+        label: '搴撲綅鍙�',
+        tableProp: 'locNo',
+        exportField: 'locNo',
+        kind: 'text',
+        valueType: 'string',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locSts',
+        columnName: 'loc_sts',
+        label: '搴撲綅鐘舵��',
+        tableProp: 'locSts',
+        exportField: 'locSts',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'row1',
+        columnName: 'row1',
+        label: '鎺�',
+        tableProp: 'row1',
+        exportField: 'row1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'bay1',
+        columnName: 'bay1',
+        label: '鍒�',
+        tableProp: 'bay1',
+        exportField: 'bay1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'lev1',
+        columnName: 'lev1',
+        label: '灞�',
+        tableProp: 'lev1',
+        exportField: 'lev1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType',
+        columnName: 'loc_type',
+        label: '搴撲綅绫诲瀷',
+        tableProp: 'locType',
+        exportField: 'locType',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locType1',
+        columnName: 'loc_type1',
+        label: '',
+        tableProp: 'locType1',
+        exportField: 'locType1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType2',
+        columnName: 'loc_type2',
+        label: '',
+        tableProp: 'locType2',
+        exportField: 'locType2',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType3',
+        columnName: 'loc_type3',
+        label: '',
+        tableProp: 'locType3',
+        exportField: 'locType3',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioTime',
+        columnName: 'io_time',
+        label: '',
+        tableProp: 'ioTime$',
+        exportField: 'ioTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorTime',
+        columnName: 'error_time',
+        label: '',
+        tableProp: 'errorTime$',
+        exportField: 'errorTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorMemo',
+        columnName: 'error_memo',
+        label: '',
+        tableProp: 'errorMemo',
+        exportField: 'errorMemo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'qrCodeValue',
+        columnName: 'qr_code_value',
+        label: '',
+        tableProp: 'qrCodeValue',
+        exportField: 'qrCodeValue',
+        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: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locNo',
+        columnName: 'loc_no',
+        label: '搴撲綅鍙�',
+        tableProp: 'locNo',
+        exportField: 'locNo',
+        kind: 'text',
+        valueType: 'string',
+        required: true,
+        primaryKey: true,
+        sortable: true,
+        textarea: false,
+        minWidth: 90,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locSts',
+        columnName: 'loc_sts',
+        label: '搴撲綅鐘舵��',
+        tableProp: 'locSts',
+        exportField: 'locSts',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'row1',
+        columnName: 'row1',
+        label: '鎺�',
+        tableProp: 'row1',
+        exportField: 'row1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'bay1',
+        columnName: 'bay1',
+        label: '鍒�',
+        tableProp: 'bay1',
+        exportField: 'bay1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'lev1',
+        columnName: 'lev1',
+        label: '灞�',
+        tableProp: 'lev1',
+        exportField: 'lev1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType',
+        columnName: 'loc_type',
+        label: '搴撲綅绫诲瀷',
+        tableProp: 'locType',
+        exportField: 'locType',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'locType1',
+        columnName: 'loc_type1',
+        label: '',
+        tableProp: 'locType1',
+        exportField: 'locType1',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType2',
+        columnName: 'loc_type2',
+        label: '',
+        tableProp: 'locType2',
+        exportField: 'locType2',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'locType3',
+        columnName: 'loc_type3',
+        label: '',
+        tableProp: 'locType3',
+        exportField: 'locType3',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioTime',
+        columnName: 'io_time',
+        label: '',
+        tableProp: 'ioTime$',
+        exportField: 'ioTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorTime',
+        columnName: 'error_time',
+        label: '',
+        tableProp: 'errorTime$',
+        exportField: 'errorTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorMemo',
+        columnName: 'error_memo',
+        label: '',
+        tableProp: 'errorMemo',
+        exportField: 'errorMemo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'qrCodeValue',
+        columnName: 'qr_code_value',
+        label: '',
+        tableProp: 'qrCodeValue',
+        exportField: 'qrCodeValue',
+        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: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#locMast',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/locMast/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        even: true,
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {type: 'checkbox', fixed: 'left'}
-            ,{field: 'locNo', align: 'center',title: '搴撲綅鍙�',sort:true}
-            ,{field: 'locSts$', align: 'center',title: '搴撲綅鐘舵��',width:200}
-            ,{field: 'barcode', align: 'center',title: '鎵樼洏鐮�'}
-            ,{field: 'row1', align: 'center',title: '鎺�', sort:true}
-            ,{field: 'bay1', align: 'center',title: '鍒�', sort:true}
-            ,{field: 'lev1', align: 'center',title: '灞�', sort:true}
-            ,{field: 'locType', align: 'center',title: '搴撲綅绫诲瀷'}
-            ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳', hide:true}
-            ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿', hide:true}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:100}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
+    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;
             }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+            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;
             }
-            pageCurr=curr;
-            limit();
-            form.on('checkbox(tableCheckbox)', function (data) {
-                var _index = $(data.elem).attr('table-index')||0;
-                if(data.elem.checked){
-                    res.data[_index][data.value] = 'Y';
-                }else{
-                    res.data[_index][data.value] = 'N';
+            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([]);
                 }
             });
-        }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(locMast)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
-            }
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(locMast)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: ['500px', top.detailHeight],
-                    shadeClose: false,
-                    content: 'locMast_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                if (data.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(data.length===1?'姝�':data.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/locMast/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {param: JSON.stringify(data)},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'locMast': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/locMast/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
-        }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(locMast)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'locMast_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit-save,#prompt', index).hide();
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                        layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: ['500px', top.detailHeight],
-                    shadeClose: false,
-                    content: 'locMast_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-save', index).hide();
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        // top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        top.convertDisabled(layer.getChildFrame('#locNo', index), true);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                        layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                    }
-                });
-                break;
-            case 'whsType':
-                var param = top.reObject(data).whsType;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '搴撲綅璇︽儏',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../basWhs/basWhs_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/basWhs/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit-save,#data-detail-submit-edit,#prompt', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-            case 'modiUser':
-                var param = top.reObject(data).modiUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '淇敼璇︽儏',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#password,#createTime\\$,#status', index).parent().parent().hide();
-                                       layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-            case 'appeUser':
-                var param = top.reObject(data).appeUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '鍒涜鎯�',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-
-        }
-    });
-
-    // 鍒濆鍖栦繚瀛�
-    form.on('submit(initDo)', function (data) {
-        $.ajax({
-            url: baseUrl+"/locMast/init/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: data.field,
-            method: 'POST',
-            async: false,
-            success: function (res) {
-                if (res.code === 200){
-                    layer.msg(res.msg);
-                    layer.closeAll();
-                    tableReload(false);
-                } else if (res.code === 403){
-                    parent.location.href = "/";
-                }else {
-                    layer.msg(res.msg)
-                }
-            }
-        })
-    });
-
-    // 鏁版嵁淇濆瓨鍔ㄤ綔
-    form.on('submit(save)', function () {
-        if (banMsg != null){
-            layer.msg(banMsg);
-            return;
-        }
-        method("add");
-    });
-
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        method("update")
-    });
-
-    function method(name){
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-            locNo: $('#locNo').val(),
-            locSts: $('#locSts').val(),
-            row1: $('#row1').val(),
-            bay1: $('#bay1').val(),
-            lev1: $('#lev1').val(),
-            locType: $('#locType').val(),
-            ioTime: top.strToDate($('#ioTime\\$').val()),
-            firstTime: top.strToDate($('#firstTime\\$').val()),
-            modiUser: $('#modiUser').val(),
-            modiTime: top.strToDate($('#modiTime\\$').val()),
-            appeUser: $('#appeUser').val(),
-            appeTime: top.strToDate($('#appeTime\\$').val()),
-            errorTime: top.strToDate($('#errorTime\\$').val()),
-            mk: $('#mk').val(),
-            barcode: $('#barcode').val(),
-        };
-        $.ajax({
-            url: baseUrl+"/locMast/"+name+"/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    parent.$(".layui-laypage-btn")[0].click();
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
-                }
-                layer.close(index);
-            }
-        })
-    }
-
-    // 澶嶉�夋浜嬩欢
-    form.on('checkbox(detailCheckbox)', function (data) {
-        var el = data.elem;
-        if (el.checked) {
-            $(el).val('Y');
-        } else {
-            $(el).val('N');
-        }
-    });
-
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    layDate.render({
-        elem: '#ioTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#firstTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#modiTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#appeTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#errorTime\\$',
-        type: 'datetime'
-    });
-
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+        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;
             }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
+            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: pageCurr-1
+                        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);
                     }
-                });
-                pageCurr -= 1;
-            }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        if (find[0]!=null){
-            if (find[0].type === 'checkbox'){
-                if (data[val]==='Y'){
-                    find.attr("checked","checked");
-                    find.val('Y');
-                } else {
-                    find.remove("checked");
-                    find.val('N');
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
-                continue;
-            }
-        }
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
-                }
-            }
-        }
+            })
+        });
     }
-}
 
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.8);
-    }
-    layer.style(index, {
-//        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
-    });
-}
-
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/login/login.js b/src/main/webapp/static/js/login/login.js
new file mode 100644
index 0000000..b12ff13
--- /dev/null
+++ b/src/main/webapp/static/js/login/login.js
@@ -0,0 +1,270 @@
+(function () {
+    function ajaxJson(options) {
+        $.ajax(options);
+    }
+
+    new Vue({
+        el: "#app",
+        data: function () {
+            return {
+                localeTick: 0,
+                localeOptions: [],
+                currentLocale: "zh-CN",
+                loginLoading: false,
+                toolsDialogVisible: false,
+                textDialogVisible: false,
+                uploadDialogVisible: false,
+                licenseBase64: "",
+                titleClickCount: 0,
+                titleClickTimer: null,
+                loginForm: {
+                    mobile: "",
+                    password: ""
+                },
+                textDialog: {
+                    title: "",
+                    label: "",
+                    text: "",
+                    tip: ""
+                },
+                loginRules: {
+                    mobile: [
+                        { required: true, message: "璇疯緭鍏ヨ处鍙�", trigger: "blur" }
+                    ],
+                    password: [
+                        { required: true, message: "璇疯緭鍏ュ瘑鐮�", trigger: "blur" }
+                    ]
+                }
+            };
+        },
+        created: function () {
+            this.initLanguageSwitch();
+        },
+        methods: {
+            text: function (key, fallback) {
+                var localeTick = this.localeTick;
+                void localeTick;
+                if (window.WCS_I18N && typeof window.WCS_I18N.t === "function") {
+                    return window.WCS_I18N.t(key);
+                }
+                return fallback || key;
+            },
+            initLanguageSwitch: function () {
+                var vm = this;
+                if (!window.WCS_I18N || typeof window.WCS_I18N.onReady !== "function") {
+                    return;
+                }
+                window.WCS_I18N.onReady(function (i18n) {
+                    vm.localeOptions = i18n.getLocaleOptions();
+                    vm.currentLocale = i18n.getLocale();
+                    document.title = i18n.t("login.title");
+                    vm.localeTick++;
+                });
+            },
+            handleLocaleChange: function () {
+                var vm = this;
+                if (!window.WCS_I18N || typeof window.WCS_I18N.setLocale !== "function") {
+                    return;
+                }
+                window.WCS_I18N.setLocale(vm.currentLocale);
+                setTimeout(function () {
+                    document.title = vm.text("login.title", "绯荤粺鐧诲綍");
+                    vm.localeTick++;
+                }, 0);
+            },
+            handleTitleClick: function () {
+                var vm = this;
+                vm.titleClickCount++;
+                if (vm.titleClickTimer) {
+                    clearTimeout(vm.titleClickTimer);
+                }
+                if (vm.titleClickCount >= 3) {
+                    vm.titleClickCount = 0;
+                    vm.toolsDialogVisible = true;
+                    return;
+                }
+                vm.titleClickTimer = setTimeout(function () {
+                    vm.titleClickCount = 0;
+                }, 500);
+            },
+            handleLogin: function () {
+                var vm = this;
+                vm.$refs.loginForm.validate(function (valid) {
+                    if (!valid) {
+                        return false;
+                    }
+                    vm.submitLogin();
+                    return true;
+                });
+            },
+            submitLogin: function () {
+                var vm = this;
+                vm.loginLoading = true;
+                ajaxJson({
+                    url: baseUrl + "/login.action",
+                    data: {
+                        mobile: vm.loginForm.mobile,
+                        password: hex_md5(vm.loginForm.password)
+                    },
+                    method: "POST",
+                    success: function (res) {
+                        if (Number(res.code) === 200) {
+                            localStorage.setItem("token", res.data.token);
+                            localStorage.setItem("username", res.data.username);
+                            window.location.href = "index.html";
+                            return;
+                        }
+                        vm.$message.error(res.msg || "鐧诲綍澶辫触");
+                    },
+                    error: function () {
+                        vm.$message.error("鐧诲綍澶辫触");
+                    },
+                    complete: function () {
+                        vm.loginLoading = false;
+                    }
+                });
+            },
+            openTextDialog: function (title, label, text, tip) {
+                var pretty = "";
+                try {
+                    pretty = typeof text === "string" ? text : JSON.stringify(text, null, 2);
+                } catch (e) {
+                    pretty = String(text || "");
+                }
+                this.textDialog = {
+                    title: title,
+                    label: label,
+                    text: pretty,
+                    tip: tip || ""
+                };
+                this.textDialogVisible = true;
+            },
+            fallbackCopy: function (text) {
+                try {
+                    var textarea = document.createElement("textarea");
+                    textarea.value = text;
+                    textarea.style.position = "fixed";
+                    textarea.style.opacity = "0";
+                    document.body.appendChild(textarea);
+                    textarea.select();
+                    document.execCommand("copy");
+                    document.body.removeChild(textarea);
+                    this.$message.success("宸插鍒跺埌鍓创鏉�");
+                } catch (err) {
+                    this.$message.error("澶嶅埗澶辫触");
+                }
+            },
+            copyText: function () {
+                var vm = this;
+                var text = vm.textDialog.text || "";
+                if (navigator.clipboard && navigator.clipboard.writeText) {
+                    navigator.clipboard.writeText(text).then(function () {
+                        vm.$message.success("宸插鍒跺埌鍓创鏉�");
+                    }).catch(function () {
+                        vm.fallbackCopy(text);
+                    });
+                    return;
+                }
+                vm.fallbackCopy(text);
+            },
+            requestCode: function () {
+                var vm = this;
+                ajaxJson({
+                    url: baseUrl + "/license/getRequestCode",
+                    method: "GET",
+                    success: function (res) {
+                        if (Number(res.code) === 200) {
+                            vm.openTextDialog("鑾峰彇璇锋眰鐮�", "璇锋眰鐮�", res.msg || "", "璇锋眰鐮佷腑宸插寘鍚」鐩悕绉帮紝鐩存帴鍙戠粰璁稿彲璇佹湇鍔$鍗冲彲銆�");
+                            return;
+                        }
+                        vm.$message.error(res.msg || "鑾峰彇璇锋眰鐮佸け璐�");
+                    },
+                    error: function () {
+                        vm.$message.error("鑾峰彇璇锋眰鐮佸け璐�");
+                    }
+                });
+            },
+            getServerInfo: function () {
+                var vm = this;
+                ajaxJson({
+                    url: baseUrl + "/license/getServerInfos",
+                    method: "GET",
+                    success: function (res) {
+                        vm.openTextDialog("鑾峰彇绯荤粺閰嶇疆", "绯荤粺閰嶇疆淇℃伅", res, "鑰侀」鐩粛鍙户缁娇鐢ㄨ繖浠界‖浠朵俊鎭� JSON 鐢宠璁稿彲璇併��");
+                    },
+                    error: function () {
+                        vm.$message.error("鑾峰彇绯荤粺閰嶇疆淇℃伅澶辫触");
+                    }
+                });
+            },
+            submitLicense: function () {
+                var vm = this;
+                if (!vm.licenseBase64) {
+                    vm.$message.warning("璁稿彲璇佸唴瀹逛笉鑳戒负绌�");
+                    return;
+                }
+                ajaxJson({
+                    url: baseUrl + "/license/updateLicense",
+                    method: "POST",
+                    contentType: "application/json;charset=UTF-8",
+                    data: JSON.stringify({ license: vm.licenseBase64 }),
+                    success: function (res) {
+                        if (Number(res.code) === 200) {
+                            vm.uploadDialogVisible = false;
+                            vm.licenseBase64 = "";
+                            vm.$message.success("璁稿彲璇佹洿鏂版垚鍔�");
+                            return;
+                        }
+                        vm.$message.error(res.msg || "璁稿彲璇佹洿鏂板け璐�");
+                    },
+                    error: function () {
+                        vm.$message.error("璁稿彲璇佸綍鍏ュけ璐�");
+                    }
+                });
+            },
+            activateLicense: function () {
+                var vm = this;
+                vm.$confirm("纭畾鎵ц涓�閿縺娲诲悧锛�", "鎻愮ず", {
+                    type: "warning",
+                    confirmButtonText: "纭畾",
+                    cancelButtonText: "鍙栨秷"
+                }).then(function () {
+                    ajaxJson({
+                        url: baseUrl + "/license/activate",
+                        method: "POST",
+                        success: function (res) {
+                            if (Number(res.code) === 200) {
+                                vm.$message.success("婵�娲绘垚鍔�");
+                                return;
+                            }
+                            vm.$message.error(res.msg || "婵�娲诲け璐�");
+                        },
+                        error: function () {
+                            vm.$message.error("婵�娲诲け璐�");
+                        }
+                    });
+                }).catch(function () {
+                });
+            },
+            getProjectName: function () {
+                var vm = this;
+                ajaxJson({
+                    url: baseUrl + "/license/getProjectName",
+                    method: "GET",
+                    success: function (res) {
+                        if (Number(res.code) === 200) {
+                            vm.$alert(res.msg || "", "椤圭洰鍚嶇О", {
+                                confirmButtonText: "纭畾"
+                            });
+                            return;
+                        }
+                        vm.$message.error(res.msg || "鑾峰彇椤圭洰鍚嶇О澶辫触");
+                    },
+                    error: function () {
+                        vm.$message.error("鑾峰彇椤圭洰鍚嶇О澶辫触");
+                    }
+                });
+            }
+        }
+    });
+})();
diff --git a/src/main/webapp/static/js/operateLog/operateLog.js b/src/main/webapp/static/js/operateLog/operateLog.js
index d8c9acf..98a653a 100644
--- a/src/main/webapp/static/js/operateLog/operateLog.js
+++ b/src/main/webapp/static/js/operateLog/operateLog.js
@@ -1,396 +1,1294 @@
-var pageCurr;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
+(function () {
+    var simpleEntityName = 'operateLog';
+    var entityName = 'OperateLog';
+    var primaryKeyField = 'id';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'action',
+        columnName: 'action',
+        label: '鎿嶄綔鍐呭',
+        tableProp: 'action',
+        exportField: 'action',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'userId',
+        columnName: 'user_id',
+        label: '鐢ㄦ埛',
+        tableProp: 'userId',
+        exportField: 'userId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ip',
+        columnName: 'ip',
+        label: '瀹㈡埛绔疘P',
+        tableProp: 'ip',
+        exportField: 'ip',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鏁版嵁',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鏁版嵁',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'action',
+        columnName: 'action',
+        label: '鎿嶄綔鍐呭',
+        tableProp: 'action',
+        exportField: 'action',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'userId',
+        columnName: 'user_id',
+        label: '鐢ㄦ埛',
+        tableProp: 'userId',
+        exportField: 'userId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ip',
+        columnName: 'ip',
+        label: '瀹㈡埛绔疘P',
+        tableProp: 'ip',
+        exportField: 'ip',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鏁版嵁',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鏁版嵁',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'action',
+        columnName: 'action',
+        label: '鎿嶄綔鍐呭',
+        tableProp: 'action',
+        exportField: 'action',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'userId',
+        columnName: 'user_id',
+        label: '鐢ㄦ埛',
+        tableProp: 'userId',
+        exportField: 'userId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ip',
+        columnName: 'ip',
+        label: '瀹㈡埛绔疘P',
+        tableProp: 'ip',
+        exportField: 'ip',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鏁版嵁',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鏁版嵁',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 'id',
+        columnName: 'id',
+        label: 'ID',
+        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: 'action',
+        columnName: 'action',
+        label: '鎿嶄綔鍐呭',
+        tableProp: 'action',
+        exportField: 'action',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'userId',
+        columnName: 'user_id',
+        label: '鐢ㄦ埛',
+        tableProp: 'userId',
+        exportField: 'userId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ip',
+        columnName: 'ip',
+        label: '瀹㈡埛绔疘P',
+        tableProp: 'ip',
+        exportField: 'ip',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'request',
+        columnName: 'request',
+        label: '璇锋眰鏁版嵁',
+        tableProp: 'request',
+        exportField: 'request',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'response',
+        columnName: 'response',
+        label: '鍝嶅簲鏁版嵁',
+        tableProp: 'response',
+        exportField: 'response',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#operateLog',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/operateLog/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        even: true,
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            // {type: 'checkbox'}
-            {field: 'id', title: 'ID', sort: true,align: 'center', width: 80}
-            ,{field: 'userId$', align: 'center',title: '鐢ㄦ埛'}
-            ,{field: 'action', align: 'center',title: '鎿嶄綔鍐呭'}
-            ,{field: 'ip', align: 'center',title: '瀹㈡埛绔疘P'}
-            ,{field: 'request', align: 'center',title: '璇锋眰鏁版嵁'}
-            ,{field: 'response', align: 'center',title: '鍝嶅簲鏁版嵁'}
-            ,{field: 'createTime$', align: 'center',title: '娣诲姞鏃堕棿'}
+    ]);
 
-            // ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:100}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-
-    table.on('edit(operateLog)', function (obj) {
-        $('body').keydown(function () {
-            if (event.keyCode === 13) {
-                layer.confirm('淇濆瓨淇敼?', function(){
-
-                });
-            }
-        });
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(operateLog)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
-            }
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(operateLog)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'operateLog_detail.html',
-                    success: function(layero, index){
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                var ids=[];
-                data.map(function (track) {
-                    ids.push(track.id);
-                });
-                if (ids.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(ids.length===1?'姝�':ids.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/operateLog/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {ids: ids},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'operateLog': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/operateLog/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
+        raw = field && field.columnName ? field.columnName : (field && field.field ? field.field : '');
+        if (!raw) {
+            return '';
         }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(operateLog)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'operateLog_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'operateLog_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            case 'userId':
-                var param = top.reObject(data).userId;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '璇︽儏',
-                       maxmin: true,
-                       area: [top.detailHeight, top.detailWidth],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-
-        }
-    });
-
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
+        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();
         });
-        var data = {
-            id: $('#id').val(),
-            action: $('#action').val(),
-            userId: $('#userId').val(),
-            ip: $('#ip').val(),
-            request: $('#request').val(),
-            response: $('#response').val(),
-            createTime: top.strToDate($('#createTime\\$').val()),
+    }
 
+    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: ''
         };
-        $.ajax({
-            url: baseUrl+"/operateLog/edit/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    tableReload(true);
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
-                }
-                layer.close(index);
+        fieldMeta.forEach(function (field) {
+            if (!isSearchableField(field)) {
+                return;
             }
-        })
-    });
+            result[field.field] = defaultSearchFieldValue(field);
+        });
+        return result;
+    }
 
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
+    function createSearchDisplayDefaults() {
+        var result = {};
+        fieldMeta.forEach(function (field) {
+            if (field.kind === 'foreign' && isSearchableField(field)) {
+                result[field.field] = '';
+            }
+        });
+        return result;
+    }
 
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
+    function createDefaultVisibleColumnKeys() {
+        return fieldMeta.map(function (field) {
+            return field.field;
+        });
+    }
 
-    // 鏃堕棿閫夋嫨鍣�
-    layDate.render({
-        elem: '#createTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '.layui-laydate-range'
-        ,type: 'datetime'
-        ,range: true
-    });
-});
+    function createFormDefaults() {
+        var result = {};
+        fieldMeta.forEach(function (field) {
+            result[field.field] = defaultFieldValue(field);
+        });
+        return result;
+    }
 
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
+    function createDisplayDefaults() {
+        var result = {};
+        fieldMeta.forEach(function (field) {
+            if (field.kind === 'foreign') {
+                result[field.field] = '';
+            }
+        });
+        return result;
+    }
 
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
+    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 === 'date' && isEmptyValue(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);
+            };
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+        fetchForeignSuggestions: function (field, queryString, callback) {
+            if (!field.foreignQuery || !queryString) {
+                callback([]);
+                return;
             }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
-                    page: {
-                        curr: pageCurr-1
+            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;
                     }
-                });
-                pageCurr -= 1;
-            }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
+                }
+            })
+        });
     }
-}
 
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.9);
-    }
-    layer.style(index, {
-        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
-    });
-    $(".layui-layer-shade").remove();
-}
-
+})();
diff --git a/src/main/webapp/static/js/password/password.js b/src/main/webapp/static/js/password/password.js
new file mode 100644
index 0000000..07c510d
--- /dev/null
+++ b/src/main/webapp/static/js/password/password.js
@@ -0,0 +1,169 @@
+(function () {
+    function resolveParentLayer() {
+        if (window.parent && window.parent.layer) {
+            return window.parent.layer;
+        }
+        if (window.top && window.top.layer) {
+            return window.top.layer;
+        }
+        return null;
+    }
+
+    function parentInputValue(id) {
+        if (!window.parent || !window.parent.document) {
+            return "";
+        }
+        var el = window.parent.document.getElementById(id);
+        return el ? (el.value || "") : "";
+    }
+
+    function closeCurrentDialog() {
+        var layer = resolveParentLayer();
+        if (!layer) {
+            window.close();
+            return;
+        }
+        var frameIndex = typeof layer.getFrameIndex === "function" ? layer.getFrameIndex(window.name) : null;
+        if (frameIndex !== null && frameIndex !== undefined && frameIndex !== -1) {
+            layer.close(frameIndex);
+            return;
+        }
+        if (typeof layer.closeAll === "function") {
+            layer.closeAll();
+        }
+    }
+
+    function handleForbidden(res) {
+        if (res && Number(res.code) === 403) {
+            top.location.href = baseUrl + "/";
+            return true;
+        }
+        return false;
+    }
+
+    new Vue({
+        el: "#passwordApp",
+        data: function () {
+            var vm = this;
+            return {
+                saving: false,
+                form: {
+                    oldPassword: "",
+                    password: "",
+                    rePassword: ""
+                },
+                rules: {
+                    oldPassword: [
+                        { required: true, message: "璇疯緭鍏ュ綋鍓嶅瘑鐮�", trigger: "blur" },
+                        {
+                            validator: function (rule, value, callback) {
+                                var currentPassword = parentInputValue("password");
+                                if (!currentPassword) {
+                                    callback(new Error("鏈鍙栧埌褰撳墠鐢ㄦ埛瀵嗙爜"));
+                                    return;
+                                }
+                                if (hex_md5(value || "") !== currentPassword) {
+                                    callback(new Error("瀵嗙爜涓嶅尮閰�"));
+                                    return;
+                                }
+                                callback();
+                            },
+                            trigger: "blur"
+                        }
+                    ],
+                    password: [
+                        { required: true, message: "璇疯緭鍏ユ柊瀵嗙爜", trigger: "blur" },
+                        {
+                            validator: function (rule, value, callback) {
+                                if (!value) {
+                                    callback(new Error("璇疯緭鍏ユ柊瀵嗙爜"));
+                                    return;
+                                }
+                                if (String(value).length < 4) {
+                                    callback(new Error("涓嶈兘灏戜簬4涓瓧绗�"));
+                                    return;
+                                }
+                                if (hex_md5(value) === parentInputValue("password")) {
+                                    callback(new Error("涓庢棫瀵嗙爜涓嶈兘鐩稿悓"));
+                                    return;
+                                }
+                                callback();
+                            },
+                            trigger: "blur"
+                        }
+                    ],
+                    rePassword: [
+                        { required: true, message: "璇峰啀娆¤緭鍏ユ柊瀵嗙爜", trigger: "blur" },
+                        {
+                            validator: function (rule, value, callback) {
+                                if (value !== vm.form.password) {
+                                    callback(new Error("瀵嗙爜涓嶄竴鑷�"));
+                                    return;
+                                }
+                                callback();
+                            },
+                            trigger: "blur"
+                        }
+                    ]
+                }
+            };
+        },
+        methods: {
+            closeDialog: function () {
+                closeCurrentDialog();
+            },
+            handleSave: function () {
+                var vm = this;
+                vm.$refs.passwordForm.validate(function (valid) {
+                    if (!valid) {
+                        return false;
+                    }
+                    vm.submitSave();
+                    return true;
+                });
+            },
+            submitSave: function () {
+                var vm = this;
+                vm.saving = true;
+                $.ajax({
+                    url: baseUrl + "/user/update/auth",
+                    headers: { token: localStorage.getItem("token") },
+                    data: {
+                        id: parentInputValue("id"),
+                        password: hex_md5(vm.form.password)
+                    },
+                    method: "POST",
+                    success: function (res) {
+                        if (handleForbidden(res)) {
+                            return;
+                        }
+                        if (Number(res.code) !== 200) {
+                            vm.$message.error(res.msg || "瀵嗙爜淇敼澶辫触");
+                            return;
+                        }
+                        if (window.parent && window.parent.document) {
+                            var passwordInput = window.parent.document.getElementById("password");
+                            if (passwordInput) {
+                                passwordInput.value = hex_md5(vm.form.password);
+                            }
+                        }
+                        closeCurrentDialog();
+                        vm.$alert("瀵嗙爜淇敼鎴愬姛锛岃閲嶆柊鐧诲綍", "鎻愮ず", {
+                            confirmButtonText: "纭畾",
+                            callback: function () {
+                                localStorage.removeItem("token");
+                                top.location.href = baseUrl + "/";
+                            }
+                        });
+                    },
+                    error: function () {
+                        vm.$message.error("瀵嗙爜淇敼澶辫触");
+                    },
+                    complete: function () {
+                        vm.saving = false;
+                    }
+                });
+            }
+        }
+    });
+})();
diff --git a/src/main/webapp/static/js/permission/permission.js b/src/main/webapp/static/js/permission/permission.js
index ca06b66..cfa4efb 100644
--- a/src/main/webapp/static/js/permission/permission.js
+++ b/src/main/webapp/static/js/permission/permission.js
@@ -1,378 +1,484 @@
-var pageCurr;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#permission',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/permission/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {type: 'checkbox', fixed: 'left'}
-            ,{field: 'id', title: 'ID', sort: true,align: 'center', fixed: 'left', width: 80}
-            ,{field: 'name', align: 'center',title: '鏉冮檺鍚嶇О'}
-            ,{field: 'action', align: 'center',title: '鎺ュ彛鍦板潃'}
-            ,{field: 'resourceName', align: 'center',title: '鎵�灞炶彍鍗�',event: 'Resource', style: 'text-decoration: underline;cursor:pointer'}
-            ,{field: 'status$', align: 'center',title: '鐘舵��'}
-
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:150}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
-        }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(permission)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
-            }
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(permission)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'permission_detail.html',
-                    success: function(layero, index){
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                var ids=[];
-                data.map(function (track) {
-                    ids.push(track.id);
-                });
-                if (ids.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(ids.length===1?'姝�':ids.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/permission/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {ids: ids},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'permission': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/permission/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
-        }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(permission)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'permission_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'permission_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            case 'Resource':
-                var param = top.reObject(data).resourceId;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '鎵�灞炶鎯�',
-                       maxmin: true,
-                       area: [top.detailHeight, top.detailWidth],
-                       shadeClose: false,
-                       content: '../resource/resource_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/resource/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-
-        }
-    });
-
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-            id: $('#id').val(),
-            name: $('#name').val(),
-            action: $('#action').val(),
-            resourceId: $('#resourceId').val(),
-            status: $('#status').val(),
-
+(function () {
+    function authHeaders() {
+        return {
+            token: localStorage.getItem('token') || ''
         };
-        $.ajax({
-            url: baseUrl+"/permission/edit/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    tableReload(true);
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
+    }
+
+    function isForbidden(res) {
+        return res && Number(res.code) === 403;
+    }
+
+    function isOk(res) {
+        return res && Number(res.code) === 200;
+    }
+
+    function normalizeId(value) {
+        if (value === null || value === undefined || value === '') {
+            return null;
+        }
+        var numberValue = Number(value);
+        return isNaN(numberValue) ? value : numberValue;
+    }
+
+    function normalizeNumber(value, fallback) {
+        if (value === null || value === undefined || value === '') {
+            return fallback;
+        }
+        var numberValue = Number(value);
+        return isNaN(numberValue) ? fallback : numberValue;
+    }
+
+    function createPermissionForm() {
+        return {
+            id: null,
+            name: '',
+            action: '',
+            resourceId: null,
+            status: 1
+        };
+    }
+
+    function escapeHtml(value) {
+        return String(value == null ? '' : value)
+            .replace(/&/g, '&amp;')
+            .replace(/</g, '&lt;')
+            .replace(/>/g, '&gt;')
+            .replace(/"/g, '&quot;')
+            .replace(/'/g, '&#39;');
+    }
+
+    function downloadExcel(filename, headers, rows) {
+        var headHtml = headers.map(function (title) {
+            return '<th style="mso-number-format:\\@;">' + escapeHtml(title) + '</th>';
+        }).join('');
+        var bodyHtml = rows.map(function (row) {
+            return '<tr>' + row.map(function (cell) {
+                return '<td style="mso-number-format:\\@;">' + escapeHtml(cell) + '</td>';
+            }).join('') + '</tr>';
+        }).join('');
+        var workbook = [
+            '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">',
+            '<head><meta charset="UTF-8"></head>',
+            '<body><table border="1"><tr>',
+            headHtml,
+            '</tr>',
+            bodyHtml,
+            '</table></body></html>'
+        ].join('');
+        var blob = new Blob([workbook], {type: 'application/vnd.ms-excel;charset=utf-8;'});
+        var link = document.createElement('a');
+        link.href = URL.createObjectURL(blob);
+        link.download = filename;
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        URL.revokeObjectURL(link.href);
+    }
+
+    new Vue({
+        el: '#app',
+        data: function () {
+            return {
+                loading: false,
+                exportLoading: false,
+                resourceSearchLoading: false,
+                dialogResourceLoading: false,
+                tableHeight: Math.max(window.innerHeight - 220, 360),
+                tableData: [],
+                selection: [],
+                pagination: {
+                    curr: 1,
+                    limit: 16,
+                    total: 0
+                },
+                searchForm: {
+                    id: '',
+                    resourceId: null
+                },
+                resourceSearchOptions: [],
+                permissionDialog: {
+                    visible: false,
+                    mode: 'create',
+                    readonly: false,
+                    submitting: false
+                },
+                permissionForm: createPermissionForm(),
+                permissionRules: {
+                    name: [
+                        {required: true, message: '璇疯緭鍏ユ潈闄愬悕绉�', trigger: 'blur'}
+                    ],
+                    action: [
+                        {required: true, message: '璇疯緭鍏ユ帴鍙e湴鍧�', trigger: 'blur'}
+                    ],
+                    status: [
+                        {required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change'}
+                    ]
+                },
+                dialogResourceOptions: [],
+                resourceDetail: {
+                    visible: false,
+                    data: {}
                 }
-                layer.close(index);
-            }
-        })
-    });
-
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
+            };
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
-                    page: {
-                        curr: pageCurr-1
+        created: function () {
+            this.loadList();
+        },
+        mounted: function () {
+            window.addEventListener('resize', this.handleResize);
+        },
+        beforeDestroy: function () {
+            window.removeEventListener('resize', this.handleResize);
+        },
+        methods: {
+            handleResize: function () {
+                this.tableHeight = Math.max(window.innerHeight - 220, 360);
+                this.refreshTableLayout();
+            },
+            handleForbidden: function (res) {
+                if (isForbidden(res)) {
+                    top.location.href = baseUrl + '/';
+                    return true;
+                }
+                return false;
+            },
+            refreshTableLayout: function () {
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.dataTable && vm.$refs.dataTable.doLayout) {
+                        vm.$refs.dataTable.doLayout();
                     }
                 });
-                pageCurr -= 1;
+            },
+            currentQuery: function () {
+                return {
+                    curr: this.pagination.curr,
+                    limit: this.pagination.limit,
+                    id: $.trim(this.searchForm.id),
+                    resourceId: this.searchForm.resourceId
+                };
+            },
+            loadList: function () {
+                var vm = this;
+                vm.loading = true;
+                $.ajax({
+                    url: baseUrl + '/permission/list/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    data: vm.currentQuery(),
+                    success: function (res) {
+                        vm.loading = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '鏉冮檺鍔犺浇澶辫触');
+                            return;
+                        }
+                        var data = res.data || {};
+                        vm.tableData = data.records || [];
+                        vm.pagination.total = data.total || 0;
+                        vm.selection = [];
+                        vm.refreshTableLayout();
+                    },
+                    error: function () {
+                        vm.loading = false;
+                        vm.$message.error('鏉冮檺鍔犺浇澶辫触');
+                    }
+                });
+            },
+            fetchResourceOptions: function (keyword, target) {
+                var vm = this;
+                var loadingKey = target === 'search' ? 'resourceSearchLoading' : 'dialogResourceLoading';
+                vm[loadingKey] = true;
+                $.ajax({
+                    url: baseUrl + '/resourceQuery/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    data: {
+                        condition: keyword || ''
+                    },
+                    success: function (res) {
+                        vm[loadingKey] = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '鑿滃崟鏌ヨ澶辫触');
+                            return;
+                        }
+                        if (target === 'search') {
+                            vm.resourceSearchOptions = res.data || [];
+                        } else {
+                            vm.dialogResourceOptions = res.data || [];
+                        }
+                    },
+                    error: function () {
+                        vm[loadingKey] = false;
+                        vm.$message.error('鑿滃崟鏌ヨ澶辫触');
+                    }
+                });
+            },
+            searchResourceOptions: function (keyword) {
+                this.fetchResourceOptions(keyword, 'search');
+            },
+            searchDialogResourceOptions: function (keyword) {
+                this.fetchResourceOptions(keyword, 'dialog');
+            },
+            ensureDialogResourceOption: function (id, label) {
+                if (!id || !label) {
+                    return;
+                }
+                var exists = this.dialogResourceOptions.some(function (item) {
+                    return normalizeId(item.id) === normalizeId(id);
+                });
+                if (!exists) {
+                    this.dialogResourceOptions = [{
+                        id: normalizeId(id),
+                        value: label
+                    }].concat(this.dialogResourceOptions);
+                }
+            },
+            handleSelectionChange: function (rows) {
+                this.selection = rows || [];
+            },
+            handleSearch: function () {
+                this.pagination.curr = 1;
+                this.loadList();
+            },
+            handleReset: function () {
+                this.searchForm.id = '';
+                this.searchForm.resourceId = null;
+                this.resourceSearchOptions = [];
+                this.pagination.curr = 1;
+                this.loadList();
+            },
+            handleCurrentChange: function (page) {
+                this.pagination.curr = page;
+                this.loadList();
+            },
+            handleSizeChange: function (size) {
+                this.pagination.limit = size;
+                this.pagination.curr = 1;
+                this.loadList();
+            },
+            resetPermissionDialog: function () {
+                this.permissionForm = createPermissionForm();
+                this.dialogResourceOptions = [];
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.permissionForm) {
+                        vm.$refs.permissionForm.clearValidate();
+                    }
+                });
+            },
+            openCreateDialog: function () {
+                this.permissionDialog.mode = 'create';
+                this.permissionDialog.readonly = false;
+                this.permissionDialog.visible = true;
+                this.resetPermissionDialog();
+            },
+            openEditDialog: function (row) {
+                this.permissionDialog.mode = 'edit';
+                this.permissionDialog.readonly = false;
+                this.permissionDialog.visible = true;
+                this.permissionForm = {
+                    id: normalizeId(row.id),
+                    name: row.name || '',
+                    action: row.action || '',
+                    resourceId: normalizeId(row.resourceId),
+                    status: normalizeNumber(row.status, 1)
+                };
+                this.dialogResourceOptions = [];
+                this.ensureDialogResourceOption(row.resourceId, row.resourceName);
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.permissionForm) {
+                        vm.$refs.permissionForm.clearValidate();
+                    }
+                });
+            },
+            openDetailDialog: function (row) {
+                this.permissionDialog.mode = 'edit';
+                this.permissionDialog.readonly = true;
+                this.permissionDialog.visible = true;
+                this.permissionForm = {
+                    id: normalizeId(row.id),
+                    name: row.name || '',
+                    action: row.action || '',
+                    resourceId: normalizeId(row.resourceId),
+                    status: normalizeNumber(row.status, 1)
+                };
+                this.dialogResourceOptions = [];
+                this.ensureDialogResourceOption(row.resourceId, row.resourceName);
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.permissionForm) {
+                        vm.$refs.permissionForm.clearValidate();
+                    }
+                });
+            },
+            submitPermission: function () {
+                var vm = this;
+                if (!vm.$refs.permissionForm) {
+                    return;
+                }
+                vm.$refs.permissionForm.validate(function (valid) {
+                    if (!valid) {
+                        return false;
+                    }
+                    vm.permissionDialog.submitting = true;
+                    $.ajax({
+                        url: baseUrl + '/permission/' + (vm.permissionDialog.mode === 'create' ? 'add' : 'update') + '/auth',
+                        method: 'POST',
+                        headers: authHeaders(),
+                        data: {
+                            id: vm.permissionDialog.mode === 'edit' ? normalizeId(vm.permissionForm.id) : null,
+                            name: $.trim(vm.permissionForm.name),
+                            action: $.trim(vm.permissionForm.action),
+                            resourceId: normalizeId(vm.permissionForm.resourceId),
+                            status: normalizeNumber(vm.permissionForm.status, 1)
+                        },
+                        success: function (res) {
+                            vm.permissionDialog.submitting = false;
+                            if (vm.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!isOk(res)) {
+                                vm.$message.error(res && res.msg ? res.msg : '淇濆瓨澶辫触');
+                                return;
+                            }
+                            vm.$message.success(res.msg || '淇濆瓨鎴愬姛');
+                            vm.permissionDialog.visible = false;
+                            vm.loadList();
+                        },
+                        error: function () {
+                            vm.permissionDialog.submitting = false;
+                            vm.$message.error('淇濆瓨澶辫触');
+                        }
+                    });
+                    return true;
+                });
+            },
+            removeSelection: function () {
+                var vm = this;
+                if (!vm.selection.length) {
+                    vm.$message.warning('璇烽�夋嫨鏁版嵁');
+                    return;
+                }
+                var ids = vm.selection.map(function (row) {
+                    return normalizeId(row.id);
+                }).filter(function (id) {
+                    return id !== null;
+                });
+                vm.$confirm('纭畾鍒犻櫎閫変腑鏉冮檺鍚楋紵', '鎻愮ず', {
+                    confirmButtonText: '纭畾',
+                    cancelButtonText: '鍙栨秷',
+                    type: 'warning'
+                }).then(function () {
+                    $.ajax({
+                        url: baseUrl + '/permission/delete/auth',
+                        method: 'POST',
+                        headers: authHeaders(),
+                        traditional: true,
+                        data: {
+                            ids: ids
+                        },
+                        success: function (res) {
+                            if (vm.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!isOk(res)) {
+                                vm.$message.error(res && res.msg ? res.msg : '鍒犻櫎澶辫触');
+                                return;
+                            }
+                            vm.$message.success(res.msg || '鍒犻櫎鎴愬姛');
+                            if (vm.tableData.length === ids.length && vm.pagination.curr > 1) {
+                                vm.pagination.curr -= 1;
+                            }
+                            vm.loadList();
+                        },
+                        error: function () {
+                            vm.$message.error('鍒犻櫎澶辫触');
+                        }
+                    });
+                }).catch(function () {
+                });
+            },
+            exportRows: function () {
+                var vm = this;
+                vm.exportLoading = true;
+                $.ajax({
+                    url: baseUrl + '/permission/export/auth',
+                    method: 'POST',
+                    headers: authHeaders(),
+                    dataType: 'json',
+                    contentType: 'application/json;charset=UTF-8',
+                    data: JSON.stringify({
+                        permission: {
+                            id: $.trim(vm.searchForm.id),
+                            resourceId: vm.searchForm.resourceId
+                        },
+                        fields: ['id', 'name', 'action', 'resourceName', 'status$']
+                    }),
+                    success: function (res) {
+                        vm.exportLoading = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '瀵煎嚭澶辫触');
+                            return;
+                        }
+                        downloadExcel('鏉冮檺绠$悊.xls', ['ID', '鏉冮檺鍚嶇О', '鎺ュ彛鍦板潃', '鎵�灞炶彍鍗�', '鐘舵��'], res.data || []);
+                    },
+                    error: function () {
+                        vm.exportLoading = false;
+                        vm.$message.error('瀵煎嚭澶辫触');
+                    }
+                });
+            },
+            openResourceDetail: function (row) {
+                var vm = this;
+                if (!row.resourceId) {
+                    vm.$message.warning('鏃犳墍灞炶彍鍗�');
+                    return;
+                }
+                $.ajax({
+                    url: baseUrl + '/resource/' + row.resourceId + '/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    success: function (res) {
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '鑿滃崟璇︽儏鍔犺浇澶辫触');
+                            return;
+                        }
+                        vm.resourceDetail.data = res.data || {};
+                        vm.resourceDetail.visible = true;
+                    },
+                    error: function () {
+                        vm.$message.error('鑿滃崟璇︽儏鍔犺浇澶辫触');
+                    }
+                });
             }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
+        },
+        watch: {
+            'permissionDialog.visible': function (visible) {
+                if (!visible) {
+                    this.permissionDialog.submitting = false;
+                    this.permissionDialog.readonly = false;
+                    this.resetPermissionDialog();
+                }
+            },
+            'resourceDetail.visible': function (visible) {
+                if (!visible) {
+                    this.resourceDetail.data = {};
                 }
             }
         }
-    }
-}
-
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.9);
-    }
-    layer.style(index, {
-        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
     });
-    $(".layui-layer-shade").remove();
-}
-
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/resource/resource.js b/src/main/webapp/static/js/resource/resource.js
index 5b5610d..1f3502c 100644
--- a/src/main/webapp/static/js/resource/resource.js
+++ b/src/main/webapp/static/js/resource/resource.js
@@ -1,382 +1,465 @@
-var pageCurr;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#resource',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/resource/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {type: 'checkbox', fixed: 'left'}
-            ,{field: 'id', title: 'ID', sort: true,align: 'center', fixed: 'left', width: 80}
-            ,{field: 'code', align: 'center',title: '鑿滃崟缂栫爜'}
-            ,{field: 'name', align: 'center',title: '鑿滃崟鍚嶇О'}
-            ,{field: 'resourceName', align: 'center',title: '鐖剁骇鑿滃崟'}
-            ,{field: 'level$', align: 'center',title: '鑿滃崟绛夌骇'}
-            ,{field: 'sort', align: 'center',title: '鎺掑簭'}
-            // ,{field: 'status$', align: 'center',title: '鐘舵��'}
-
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:150}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
-        }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(resource)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
-            }
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(resource)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'resource_detail.html',
-                    success: function(layero, index){
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                var ids=[];
-                data.map(function (track) {
-                    ids.push(track.id);
-                });
-                if (ids.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(ids.length===1?'姝�':ids.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/resource/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {ids: ids},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'resource': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/resource/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
-        }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(resource)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'resource_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'resource_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            case 'Resource':
-                var param = top.reObject(data).resourceId;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '鐖剁骇璇︽儏',
-                       maxmin: true,
-                       area: [top.detailHeight, top.detailWidth],
-                       shadeClose: false,
-                       content: '../resource/resource_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/resource/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-
-        }
-    });
-
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-            id: $('#id').val(),
-            code: $('#code').val(),
-            name: $('#name').val(),
-            resourceId: $('#resourceId').val(),
-            level: $('#level').val(),
-            sort: $('#sort').val(),
-            status: $('#status').val(),
-
+(function () {
+    function authHeaders() {
+        return {
+            token: localStorage.getItem('token') || ''
         };
-        $.ajax({
-            url: baseUrl+"/resource/edit/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    tableReload(true);
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
+    }
+
+    function isForbidden(res) {
+        return res && Number(res.code) === 403;
+    }
+
+    function isOk(res) {
+        return res && (Number(res.code) === 200 || Number(res.code) === 0);
+    }
+
+    function normalizeId(value) {
+        if (value === null || value === undefined || value === '') {
+            return null;
+        }
+        var numberValue = Number(value);
+        return isNaN(numberValue) ? value : numberValue;
+    }
+
+    function normalizeNumber(value, fallback) {
+        if (value === null || value === undefined || value === '') {
+            return fallback;
+        }
+        var numberValue = Number(value);
+        return isNaN(numberValue) ? fallback : numberValue;
+    }
+
+    function cloneRow(row) {
+        return {
+            id: normalizeId(row.id),
+            code: row.code || '',
+            name: row.name || '',
+            resourceId: normalizeId(row.resourceId),
+            level: normalizeNumber(row.level, 1),
+            sort: normalizeNumber(row.sort, 999),
+            status: normalizeNumber(row.status, 1)
+        };
+    }
+
+    function createDefaultForm() {
+        return {
+            id: null,
+            resourceId: null,
+            code: '',
+            level: 1,
+            name: '',
+            sort: 999,
+            status: 1
+        };
+    }
+
+    function sortNodes(nodes) {
+        nodes.sort(function (left, right) {
+            var sortDiff = normalizeNumber(left.sort, 999) - normalizeNumber(right.sort, 999);
+            if (sortDiff !== 0) {
+                return sortDiff;
+            }
+            return String(left.id).localeCompare(String(right.id), 'zh-Hans-CN', {numeric: true});
+        });
+        nodes.forEach(function (node) {
+            if (node.children && node.children.length > 0) {
+                sortNodes(node.children);
+            }
+        });
+        return nodes;
+    }
+
+    function buildTree(rows) {
+        var nodeMap = {};
+        var roots = [];
+
+        rows.forEach(function (item) {
+            var node = cloneRow(item);
+            node.children = [];
+            nodeMap[node.id] = node;
+        });
+
+        Object.keys(nodeMap).forEach(function (key) {
+            var current = nodeMap[key];
+            if (current.resourceId !== null && nodeMap[current.resourceId] && current.resourceId !== current.id) {
+                nodeMap[current.resourceId].children.push(current);
+            } else {
+                roots.push(current);
+            }
+        });
+
+        return sortNodes(roots);
+    }
+
+    new Vue({
+        el: '#app',
+        data: function () {
+            var vm = this;
+            return {
+                loading: false,
+                tableData: [],
+                tableKey: 0,
+                tableHeight: Math.max(window.innerHeight - 198, 360),
+                expandAll: false,
+                selection: [],
+                flatRows: [],
+                resourceLookup: {},
+                childrenLookup: {},
+                editingId: null,
+                dialog: {
+                    visible: false,
+                    mode: 'create',
+                    submitting: false
+                },
+                dialogForm: createDefaultForm(),
+                parentCascaderProps: {
+                    checkStrictly: true,
+                    emitPath: false,
+                    value: 'id',
+                    label: 'name',
+                    children: 'children'
+                },
+                dialogRules: {
+                    code: [
+                        {required: true, message: '璇疯緭鍏ヨ彍鍗曠紪鐮�', trigger: 'blur'}
+                    ],
+                    name: [
+                        {required: true, message: '璇疯緭鍏ヨ彍鍗曞悕绉�', trigger: 'blur'}
+                    ],
+                    level: [
+                        {required: true, message: '璇烽�夋嫨绫诲瀷', trigger: 'change'}
+                    ],
+                    resourceId: [
+                        {
+                            validator: function (rule, value, callback) {
+                                if (Number(vm.dialogForm.level) > 1 && (value === null || value === undefined || value === '')) {
+                                    callback(new Error('璇烽�夋嫨涓婄骇鑿滃崟'));
+                                    return;
+                                }
+                                callback();
+                            },
+                            trigger: 'change'
+                        }
+                    ],
+                    sort: [
+                        {type: 'number', message: '鎺掑簭蹇呴』涓烘暟瀛�', trigger: 'blur'}
+                    ],
+                    status: [
+                        {required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change'}
+                    ]
                 }
-                layer.close(index);
-            }
-        })
-    });
-
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
+            };
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+        computed: {
+            parentOptions: function () {
+                var blocked = {};
+                if (this.editingId !== null) {
+                    blocked = this.collectDescendantIds(this.editingId);
+                }
+                return this.buildParentOptions(this.tableData, blocked);
             }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
-                    page: {
-                        curr: pageCurr-1
+        },
+        created: function () {
+            this.loadTree();
+        },
+        mounted: function () {
+            window.addEventListener('resize', this.handleResize);
+        },
+        beforeDestroy: function () {
+            window.removeEventListener('resize', this.handleResize);
+        },
+        methods: {
+            createDefaultForm: function () {
+                return createDefaultForm();
+            },
+            handleResize: function () {
+                this.tableHeight = Math.max(window.innerHeight - 198, 360);
+                this.refreshTableLayout();
+            },
+            handleForbidden: function (res) {
+                if (isForbidden(res)) {
+                    top.location.href = baseUrl + '/';
+                    return true;
+                }
+                return false;
+            },
+            refreshTableLayout: function () {
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.treeTable && vm.$refs.treeTable.doLayout) {
+                        vm.$refs.treeTable.doLayout();
                     }
                 });
-                pageCurr -= 1;
-            }
-            limit(child);
-        }
-    });
-}
+            },
+            rebuildIndexes: function (rows) {
+                var lookup = {};
+                var childrenLookup = {};
 
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
+                rows.forEach(function (item) {
+                    var normalized = cloneRow(item);
+                    lookup[normalized.id] = normalized;
+                    if (normalized.resourceId !== null) {
+                        if (!childrenLookup[normalized.resourceId]) {
+                            childrenLookup[normalized.resourceId] = [];
+                        }
+                        childrenLookup[normalized.resourceId].push(normalized.id);
+                    }
+                });
+
+                this.flatRows = rows.map(function (item) {
+                    return cloneRow(item);
+                });
+                this.resourceLookup = lookup;
+                this.childrenLookup = childrenLookup;
+            },
+            loadTree: function () {
+                var vm = this;
+                vm.loading = true;
+                $.ajax({
+                    url: baseUrl + '/resource/tree/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    success: function (res) {
+                        vm.loading = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '鑿滃崟鍔犺浇澶辫触');
+                            return;
+                        }
+                        var rows = Array.isArray(res.data) ? res.data : [];
+                        vm.rebuildIndexes(rows);
+                        vm.tableData = buildTree(rows);
+                        vm.selection = [];
+                        vm.tableKey += 1;
+                        vm.refreshTableLayout();
+                    },
+                    error: function () {
+                        vm.loading = false;
+                        vm.$message.error('鑿滃崟鍔犺浇澶辫触');
+                    }
+                });
+            },
+            handleSelectionChange: function (rows) {
+                this.selection = rows || [];
+            },
+            toggleExpandAll: function () {
+                this.expandAll = !this.expandAll;
+                this.tableKey += 1;
+                this.refreshTableLayout();
+            },
+            levelText: function (level) {
+                var currentLevel = Number(level);
+                if (currentLevel === 1) {
+                    return '涓�绾ц彍鍗�';
+                }
+                if (currentLevel === 2) {
+                    return '浜岀骇鑿滃崟';
+                }
+                if (currentLevel === 3) {
+                    return '鎸夐挳';
+                }
+                return '--';
+            },
+            levelTagType: function (level) {
+                var currentLevel = Number(level);
+                if (currentLevel === 3) {
+                    return 'info';
+                }
+                if (currentLevel === 2) {
+                    return '';
+                }
+                return 'success';
+            },
+            parentName: function (resourceId) {
+                if (resourceId === null || resourceId === undefined || resourceId === '') {
+                    return '';
+                }
+                var parent = this.resourceLookup[normalizeId(resourceId)];
+                return parent ? parent.name : '';
+            },
+            collectDescendantIds: function (resourceId) {
+                var blocked = {};
+                var stack = [normalizeId(resourceId)];
+                while (stack.length > 0) {
+                    var currentId = stack.pop();
+                    if (currentId === null || blocked[currentId]) {
+                        continue;
+                    }
+                    blocked[currentId] = true;
+                    var childIds = this.childrenLookup[currentId] || [];
+                    childIds.forEach(function (childId) {
+                        stack.push(childId);
+                    });
+                }
+                return blocked;
+            },
+            buildParentOptions: function (nodes, blocked) {
+                var vm = this;
+                return (nodes || []).reduce(function (result, node) {
+                    if (blocked[node.id]) {
+                        return result;
+                    }
+                    var option = {
+                        id: node.id,
+                        name: node.name,
+                        children: vm.buildParentOptions(node.children || [], blocked)
+                    };
+                    if (option.children.length === 0) {
+                        delete option.children;
+                    }
+                    result.push(option);
+                    return result;
+                }, []);
+            },
+            resetDialogForm: function () {
+                this.editingId = null;
+                this.dialogForm = this.createDefaultForm();
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.dialogForm) {
+                        vm.$refs.dialogForm.clearValidate();
+                    }
+                });
+            },
+            openCreateDialog: function () {
+                this.dialog.mode = 'create';
+                this.dialog.visible = true;
+                this.resetDialogForm();
+            },
+            openEditDialog: function (row) {
+                this.dialog.mode = 'edit';
+                this.dialog.visible = true;
+                this.editingId = normalizeId(row.id);
+                this.dialogForm = {
+                    id: normalizeId(row.id),
+                    resourceId: normalizeId(row.resourceId),
+                    code: row.code || '',
+                    level: normalizeNumber(row.level, 1),
+                    name: row.name || '',
+                    sort: normalizeNumber(row.sort, 999),
+                    status: normalizeNumber(row.status, 1)
+                };
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.dialogForm) {
+                        vm.$refs.dialogForm.clearValidate();
+                    }
+                });
+            },
+            removeSelection: function () {
+                if (!this.selection.length) {
+                    this.$message.warning('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁');
+                    return;
+                }
+                var ids = this.selection.map(function (row) {
+                    return normalizeId(row.id);
+                }).filter(function (id) {
+                    return id !== null;
+                });
+                this.removeRows(ids);
+            },
+            removeRows: function (ids) {
+                var vm = this;
+                var uniqueIds = Array.from(new Set((ids || []).map(function (id) {
+                    return normalizeId(id);
+                }).filter(function (id) {
+                    return id !== null;
+                })));
+                if (!uniqueIds.length) {
+                    vm.$message.warning('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁');
+                    return;
+                }
+                vm.$confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', '鎻愮ず', {
+                    confirmButtonText: '纭畾',
+                    cancelButtonText: '鍙栨秷',
+                    type: 'warning'
+                }).then(function () {
+                    $.ajax({
+                        url: baseUrl + '/resource/delete/auth',
+                        method: 'POST',
+                        headers: authHeaders(),
+                        traditional: true,
+                        data: {
+                            ids: uniqueIds
+                        },
+                        success: function (res) {
+                            if (vm.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!isOk(res)) {
+                                vm.$message.error(res && res.msg ? res.msg : '鍒犻櫎澶辫触');
+                                return;
+                            }
+                            vm.$message.success(res.msg || '鍒犻櫎鎴愬姛');
+                            vm.loadTree();
+                        },
+                        error: function () {
+                            vm.$message.error('鍒犻櫎澶辫触');
+                        }
+                    });
+                }).catch(function () {
+                });
+            },
+            submitDialog: function () {
+                var vm = this;
+                if (!vm.$refs.dialogForm) {
+                    return;
+                }
+                vm.$refs.dialogForm.validate(function (valid) {
+                    if (!valid) {
+                        return false;
+                    }
+                    var payload = {
+                        id: vm.dialog.mode === 'edit' ? normalizeId(vm.dialogForm.id) : null,
+                        resourceId: Number(vm.dialogForm.level) === 1 ? null : normalizeId(vm.dialogForm.resourceId),
+                        code: $.trim(vm.dialogForm.code),
+                        level: normalizeNumber(vm.dialogForm.level, 1),
+                        name: $.trim(vm.dialogForm.name),
+                        sort: normalizeNumber(vm.dialogForm.sort, 999),
+                        status: normalizeNumber(vm.dialogForm.status, 1)
+                    };
+                    vm.dialog.submitting = true;
+                    $.ajax({
+                        url: baseUrl + '/resource/' + (vm.dialog.mode === 'create' ? 'add' : 'update') + '/auth',
+                        method: 'POST',
+                        headers: authHeaders(),
+                        data: payload,
+                        success: function (res) {
+                            vm.dialog.submitting = false;
+                            if (vm.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!isOk(res)) {
+                                vm.$message.error(res && res.msg ? res.msg : '淇濆瓨澶辫触');
+                                return;
+                            }
+                            vm.$message.success(res.msg || '淇濆瓨鎴愬姛');
+                            vm.dialog.visible = false;
+                            vm.loadTree();
+                        },
+                        error: function () {
+                            vm.dialog.submitting = false;
+                            vm.$message.error('淇濆瓨澶辫触');
+                        }
+                    });
+                    return true;
+                });
+            }
+        },
+        watch: {
+            'dialogForm.level': function (value) {
+                if (Number(value) === 1) {
+                    this.dialogForm.resourceId = null;
+                }
+            },
+            'dialog.visible': function (visible) {
+                if (!visible) {
+                    this.resetDialogForm();
+                    this.dialog.submitting = false;
                 }
             }
         }
-    }
-}
-
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.9);
-    }
-    layer.style(index, {
-        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
     });
-    $(".layui-layer-shade").remove();
-}
-
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/role/role.js b/src/main/webapp/static/js/role/role.js
index 3bdce29..8d6f6ec 100644
--- a/src/main/webapp/static/js/role/role.js
+++ b/src/main/webapp/static/js/role/role.js
@@ -1,410 +1,572 @@
-var pageCurr;
-var roleId;
-var powerTreeData;
-$(function (){
-    $.ajax({
-        url: baseUrl+"/power/list/auth",
-        headers: {'token': localStorage.getItem('token')},
-        method: 'GET',
-        success: function (res) {
-            if (res.code === 200){
-                powerTreeData = res.data;
-            } else if (res.code === 403){
-                top.location.href = baseUrl+"/";
-            } else {
-                layer.msg(res.msg)
-            }
-        }
-    })
-});
-
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#role',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/role/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {type: 'checkbox', fixed: 'left'}
-            ,{field: 'id', title: 'ID', sort: true,align: 'center', fixed: 'left', width: 80}
-            ,{field: 'code', align: 'center',title: '缂栫爜'}
-            ,{field: 'name', align: 'center',title: '鍚嶇О'}
-            ,{field: 'leader$', align: 'center',title: '涓婄骇'}
-            // ,{field: 'level$', align: 'center',title: '瑙掕壊绛夌骇'}
-
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:150}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
-        }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(role)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
-            }
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(role)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'role_detail.html',
-                    success: function(layero, index){
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                var ids=[];
-                data.map(function (track) {
-                    ids.push(track.id);
-                });
-                if (ids.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(ids.length===1?'姝�':ids.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/role/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {ids: ids},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'role': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/role/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
-        }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(role)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'role_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'role_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            case 'leader':
-                var param = top.reObject(data).leader;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '璇︽儏',
-                       maxmin: true,
-                       area: [top.detailHeight, top.detailWidth],
-                       shadeClose: false,
-                       content: '../role/role_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/role/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-            case 'power':
-                roleId = data.id;
-                layer.open({
-                    type: 2,
-                    title: data.name + ' 鏉冮檺鍒嗛厤',
-                    maxmin: true,
-                    area: [top.detailWidth/2, '85%'],
-                    shadeClose: false,
-                    content: 'role_power_detail.html',
-                    success: function(layero, index){
-                    }
-                });
-                break;
-        }
-    });
-
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-            id: $('#id').val(),
-            code: $('#code').val(),
-            name: $('#name').val(),
-            leader: $('#leader').val(),
-            level: $('#level').val(),
-
+(function () {
+    function authHeaders() {
+        return {
+            token: localStorage.getItem('token') || ''
         };
-        $.ajax({
-            url: baseUrl+"/role/edit/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    tableReload(true);
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
-                }
-                layer.close(index);
-            }
-        })
-    });
+    }
 
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
+    function isForbidden(res) {
+        return res && Number(res.code) === 403;
+    }
 
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
+    function isOk(res) {
+        return res && Number(res.code) === 200;
+    }
 
-    // 鏃堕棿閫夋嫨鍣�
+    function normalizeId(value) {
+        if (value === null || value === undefined || value === '') {
+            return null;
+        }
+        var numberValue = Number(value);
+        return isNaN(numberValue) ? value : numberValue;
+    }
 
+    function createRoleForm() {
+        return {
+            id: null,
+            code: '',
+            name: '',
+            leader: null
+        };
+    }
 
-});
+    function escapeHtml(value) {
+        return String(value == null ? '' : value)
+            .replace(/&/g, '&amp;')
+            .replace(/</g, '&lt;')
+            .replace(/>/g, '&gt;')
+            .replace(/"/g, '&quot;')
+            .replace(/'/g, '&#39;');
+    }
 
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
+    function downloadExcel(filename, headers, rows) {
+        var headHtml = headers.map(function (title) {
+            return '<th style="mso-number-format:\\@;">' + escapeHtml(title) + '</th>';
+        }).join('');
+        var bodyHtml = rows.map(function (row) {
+            return '<tr>' + row.map(function (cell) {
+                return '<td style="mso-number-format:\\@;">' + escapeHtml(cell) + '</td>';
+            }).join('') + '</tr>';
+        }).join('');
+        var workbook = [
+            '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">',
+            '<head><meta charset="UTF-8"></head>',
+            '<body><table border="1"><tr>',
+            headHtml,
+            '</tr>',
+            bodyHtml,
+            '</table></body></html>'
+        ].join('');
+        var blob = new Blob([workbook], {type: 'application/vnd.ms-excel;charset=utf-8;'});
+        var link = document.createElement('a');
+        link.href = URL.createObjectURL(blob);
+        link.download = filename;
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        URL.revokeObjectURL(link.href);
+    }
 
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
+    new Vue({
+        el: '#app',
+        data: function () {
+            return {
+                loading: false,
+                exportLoading: false,
+                leaderSearchLoading: false,
+                dialogLeaderLoading: false,
+                tableHeight: Math.max(window.innerHeight - 220, 360),
+                tableData: [],
+                selection: [],
+                pagination: {
+                    curr: 1,
+                    limit: 16,
+                    total: 0
+                },
+                searchForm: {
+                    leader: null
+                },
+                leaderSearchOptions: [],
+                roleDialog: {
+                    visible: false,
+                    mode: 'create',
+                    submitting: false
+                },
+                roleForm: createRoleForm(),
+                roleRules: {
+                    code: [
+                        {required: true, message: '璇疯緭鍏ョ紪鐮�', trigger: 'blur'}
+                    ],
+                    name: [
+                        {required: true, message: '璇疯緭鍏ュ悕绉�', trigger: 'blur'}
+                    ]
+                },
+                dialogLeaderOptions: [],
+                powerDialog: {
+                    visible: false,
+                    roleId: null,
+                    roleName: '',
+                    loading: false,
+                    submitting: false
+                },
+                powerTreeData: [],
+                powerExpandedKeys: []
+            };
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
-                    page: {
-                        curr: pageCurr-1
+        created: function () {
+            this.loadList();
+        },
+        mounted: function () {
+            window.addEventListener('resize', this.handleResize);
+        },
+        beforeDestroy: function () {
+            window.removeEventListener('resize', this.handleResize);
+        },
+        methods: {
+            handleResize: function () {
+                this.tableHeight = Math.max(window.innerHeight - 220, 360);
+                this.refreshTableLayout();
+            },
+            handleForbidden: function (res) {
+                if (isForbidden(res)) {
+                    top.location.href = baseUrl + '/';
+                    return true;
+                }
+                return false;
+            },
+            refreshTableLayout: function () {
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.dataTable && vm.$refs.dataTable.doLayout) {
+                        vm.$refs.dataTable.doLayout();
                     }
                 });
-                pageCurr -= 1;
+            },
+            currentQuery: function () {
+                return {
+                    curr: this.pagination.curr,
+                    limit: this.pagination.limit,
+                    leader: this.searchForm.leader
+                };
+            },
+            loadList: function () {
+                var vm = this;
+                vm.loading = true;
+                $.ajax({
+                    url: baseUrl + '/role/list/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    data: vm.currentQuery(),
+                    success: function (res) {
+                        vm.loading = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '瑙掕壊鍔犺浇澶辫触');
+                            return;
+                        }
+                        var data = res.data || {};
+                        vm.tableData = data.records || [];
+                        vm.pagination.total = data.total || 0;
+                        vm.selection = [];
+                        vm.refreshTableLayout();
+                    },
+                    error: function () {
+                        vm.loading = false;
+                        vm.$message.error('瑙掕壊鍔犺浇澶辫触');
+                    }
+                });
+            },
+            fetchRoleOptions: function (keyword, target) {
+                var vm = this;
+                var loadingKey = target === 'search' ? 'leaderSearchLoading' : 'dialogLeaderLoading';
+                vm[loadingKey] = true;
+                $.ajax({
+                    url: baseUrl + '/roleQuery/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    data: {
+                        condition: keyword || ''
+                    },
+                    success: function (res) {
+                        vm[loadingKey] = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '瑙掕壊鏌ヨ澶辫触');
+                            return;
+                        }
+                        if (target === 'search') {
+                            vm.leaderSearchOptions = res.data || [];
+                        } else {
+                            vm.dialogLeaderOptions = res.data || [];
+                        }
+                    },
+                    error: function () {
+                        vm[loadingKey] = false;
+                        vm.$message.error('瑙掕壊鏌ヨ澶辫触');
+                    }
+                });
+            },
+            searchLeaderOptions: function (keyword) {
+                this.fetchRoleOptions(keyword, 'search');
+            },
+            searchDialogLeaderOptions: function (keyword) {
+                this.fetchRoleOptions(keyword, 'dialog');
+            },
+            ensureDialogLeaderOption: function (id, label) {
+                if (!id || !label) {
+                    return;
+                }
+                var exists = this.dialogLeaderOptions.some(function (item) {
+                    return normalizeId(item.id) === normalizeId(id);
+                });
+                if (!exists) {
+                    this.dialogLeaderOptions = [{
+                        id: normalizeId(id),
+                        value: label
+                    }].concat(this.dialogLeaderOptions);
+                }
+            },
+            handleSelectionChange: function (rows) {
+                this.selection = rows || [];
+            },
+            handleSearch: function () {
+                this.pagination.curr = 1;
+                this.loadList();
+            },
+            handleReset: function () {
+                this.searchForm.leader = null;
+                this.leaderSearchOptions = [];
+                this.pagination.curr = 1;
+                this.loadList();
+            },
+            handleCurrentChange: function (page) {
+                this.pagination.curr = page;
+                this.loadList();
+            },
+            handleSizeChange: function (size) {
+                this.pagination.limit = size;
+                this.pagination.curr = 1;
+                this.loadList();
+            },
+            resetRoleDialog: function () {
+                this.roleForm = createRoleForm();
+                this.dialogLeaderOptions = [];
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.roleForm) {
+                        vm.$refs.roleForm.clearValidate();
+                    }
+                });
+            },
+            openCreateDialog: function () {
+                this.roleDialog.mode = 'create';
+                this.roleDialog.visible = true;
+                this.resetRoleDialog();
+            },
+            openEditDialog: function (row) {
+                this.roleDialog.mode = 'edit';
+                this.roleDialog.visible = true;
+                this.roleForm = {
+                    id: normalizeId(row.id),
+                    code: row.code || '',
+                    name: row.name || '',
+                    leader: normalizeId(row.leader)
+                };
+                this.dialogLeaderOptions = [];
+                this.ensureDialogLeaderOption(row.leader, row['leader$']);
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.roleForm) {
+                        vm.$refs.roleForm.clearValidate();
+                    }
+                });
+            },
+            submitRole: function () {
+                var vm = this;
+                if (!vm.$refs.roleForm) {
+                    return;
+                }
+                vm.$refs.roleForm.validate(function (valid) {
+                    if (!valid) {
+                        return false;
+                    }
+                    vm.roleDialog.submitting = true;
+                    $.ajax({
+                        url: baseUrl + '/role/' + (vm.roleDialog.mode === 'create' ? 'add' : 'update') + '/auth',
+                        method: 'POST',
+                        headers: authHeaders(),
+                        data: {
+                            id: vm.roleDialog.mode === 'edit' ? normalizeId(vm.roleForm.id) : null,
+                            code: $.trim(vm.roleForm.code),
+                            name: $.trim(vm.roleForm.name),
+                            leader: normalizeId(vm.roleForm.leader)
+                        },
+                        success: function (res) {
+                            vm.roleDialog.submitting = false;
+                            if (vm.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!isOk(res)) {
+                                vm.$message.error(res && res.msg ? res.msg : '淇濆瓨澶辫触');
+                                return;
+                            }
+                            vm.$message.success(res.msg || '淇濆瓨鎴愬姛');
+                            vm.roleDialog.visible = false;
+                            vm.loadList();
+                        },
+                        error: function () {
+                            vm.roleDialog.submitting = false;
+                            vm.$message.error('淇濆瓨澶辫触');
+                        }
+                    });
+                    return true;
+                });
+            },
+            removeSelection: function () {
+                var vm = this;
+                if (!vm.selection.length) {
+                    vm.$message.warning('璇烽�夋嫨鏁版嵁');
+                    return;
+                }
+                var ids = vm.selection.map(function (row) {
+                    return normalizeId(row.id);
+                }).filter(function (id) {
+                    return id !== null;
+                });
+                vm.$confirm('纭畾鍒犻櫎閫変腑瑙掕壊鍚楋紵', '鎻愮ず', {
+                    confirmButtonText: '纭畾',
+                    cancelButtonText: '鍙栨秷',
+                    type: 'warning'
+                }).then(function () {
+                    $.ajax({
+                        url: baseUrl + '/role/delete/auth',
+                        method: 'POST',
+                        headers: authHeaders(),
+                        traditional: true,
+                        data: {
+                            ids: ids
+                        },
+                        success: function (res) {
+                            if (vm.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!isOk(res)) {
+                                vm.$message.error(res && res.msg ? res.msg : '鍒犻櫎澶辫触');
+                                return;
+                            }
+                            vm.$message.success(res.msg || '鍒犻櫎鎴愬姛');
+                            if (vm.tableData.length === ids.length && vm.pagination.curr > 1) {
+                                vm.pagination.curr -= 1;
+                            }
+                            vm.loadList();
+                        },
+                        error: function () {
+                            vm.$message.error('鍒犻櫎澶辫触');
+                        }
+                    });
+                }).catch(function () {
+                });
+            },
+            exportRows: function () {
+                var vm = this;
+                vm.exportLoading = true;
+                $.ajax({
+                    url: baseUrl + '/role/export/auth',
+                    method: 'POST',
+                    headers: authHeaders(),
+                    dataType: 'json',
+                    contentType: 'application/json;charset=UTF-8',
+                    data: JSON.stringify({
+                        role: {
+                            leader: vm.searchForm.leader
+                        },
+                        fields: ['id', 'code', 'name', 'leader$']
+                    }),
+                    success: function (res) {
+                        vm.exportLoading = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '瀵煎嚭澶辫触');
+                            return;
+                        }
+                        downloadExcel('瑙掕壊绠$悊.xls', ['ID', '缂栫爜', '鍚嶇О', '涓婄骇'], res.data || []);
+                    },
+                    error: function () {
+                        vm.exportLoading = false;
+                        vm.$message.error('瀵煎嚭澶辫触');
+                    }
+                });
+            },
+            ensurePowerTree: function (callback) {
+                var vm = this;
+                if (vm.powerTreeData.length) {
+                    if (callback) {
+                        callback();
+                    }
+                    return;
+                }
+                $.ajax({
+                    url: baseUrl + '/power/list/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    success: function (res) {
+                        if (vm.handleForbidden(res)) {
+                            vm.powerDialog.loading = false;
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.powerDialog.loading = false;
+                            vm.$message.error(res && res.msg ? res.msg : '鏉冮檺鏍戝姞杞藉け璐�');
+                            return;
+                        }
+                        vm.powerTreeData = res.data || [];
+                        vm.powerExpandedKeys = [];
+                        if (callback) {
+                            callback();
+                        }
+                    },
+                    error: function () {
+                        vm.powerDialog.loading = false;
+                        vm.$message.error('鏉冮檺鏍戝姞杞藉け璐�');
+                    }
+                });
+            },
+            loadRolePower: function () {
+                var vm = this;
+                vm.powerDialog.loading = true;
+                $.ajax({
+                    url: baseUrl + '/power/' + vm.powerDialog.roleId + '/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    success: function (res) {
+                        vm.powerDialog.loading = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '鏉冮檺鍥炴樉澶辫触');
+                            return;
+                        }
+                        vm.$nextTick(function () {
+                            if (vm.$refs.powerTree) {
+                                vm.$refs.powerTree.setCheckedKeys(res.data || []);
+                            }
+                        });
+                    },
+                    error: function () {
+                        vm.powerDialog.loading = false;
+                        vm.$message.error('鏉冮檺鍥炴樉澶辫触');
+                    }
+                });
+            },
+            openPowerDialog: function (row) {
+                var vm = this;
+                vm.powerDialog.visible = true;
+                vm.powerDialog.roleId = normalizeId(row.id);
+                vm.powerDialog.roleName = row.name || '';
+                vm.powerDialog.loading = true;
+                vm.ensurePowerTree(function () {
+                    vm.$nextTick(function () {
+                        if (vm.$refs.powerTree) {
+                            vm.$refs.powerTree.setCheckedKeys([]);
+                        }
+                        vm.loadRolePower();
+                    });
+                });
+            },
+            collectPowerPayload: function () {
+                var tree = this.$refs.powerTree;
+                if (!tree) {
+                    return [];
+                }
+                var selected = tree.getCheckedKeys().concat(tree.getHalfCheckedKeys());
+                var selectedMap = {};
+                selected.forEach(function (key) {
+                    selectedMap[key] = true;
+                });
+                var payload = [];
+                this.powerTreeData.forEach(function (top) {
+                    (top.children || []).forEach(function (node) {
+                        var childList = node.children || [];
+                        var checkedChildren = childList.filter(function (child) {
+                            return !!selectedMap[child.id];
+                        }).map(function (child) {
+                            return String(child.id);
+                        });
+                        if (childList.length === 0) {
+                            if (selectedMap[node.id]) {
+                                payload.push({
+                                    two: node.id,
+                                    three: []
+                                });
+                            }
+                            return;
+                        }
+                        if (selectedMap[node.id] || checkedChildren.length > 0) {
+                            payload.push({
+                                two: node.id,
+                                three: checkedChildren
+                            });
+                        }
+                    });
+                });
+                return payload;
+            },
+            submitPower: function () {
+                var vm = this;
+                vm.powerDialog.submitting = true;
+                $.ajax({
+                    url: baseUrl + '/power/auth',
+                    method: 'POST',
+                    headers: authHeaders(),
+                    traditional: true,
+                    data: {
+                        roleId: vm.powerDialog.roleId,
+                        powers: JSON.stringify(vm.collectPowerPayload())
+                    },
+                    success: function (res) {
+                        vm.powerDialog.submitting = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '鏉冮檺淇濆瓨澶辫触');
+                            return;
+                        }
+                        vm.$message.success(res.msg || '淇濆瓨鎴愬姛');
+                        vm.powerDialog.visible = false;
+                    },
+                    error: function () {
+                        vm.powerDialog.submitting = false;
+                        vm.$message.error('鏉冮檺淇濆瓨澶辫触');
+                    }
+                });
             }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
+        },
+        watch: {
+            'roleDialog.visible': function (visible) {
+                if (!visible) {
+                    this.resetRoleDialog();
+                    this.roleDialog.submitting = false;
+                }
+            },
+            'powerDialog.visible': function (visible) {
+                if (!visible) {
+                    this.powerDialog.roleId = null;
+                    this.powerDialog.roleName = '';
+                    this.powerDialog.loading = false;
+                    this.powerDialog.submitting = false;
+                    if (this.$refs.powerTree) {
+                        this.$refs.powerTree.setCheckedKeys([]);
+                    }
                 }
             }
         }
-    }
-}
-
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.9);
-    }
-    layer.style(index, {
-        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
     });
-    $(".layui-layer-shade").remove();
-}
-
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/user/user.js b/src/main/webapp/static/js/user/user.js
index 0e06c7e..84396c6 100644
--- a/src/main/webapp/static/js/user/user.js
+++ b/src/main/webapp/static/js/user/user.js
@@ -1,238 +1,3379 @@
-var pageCurr;
-var tableData;
-var insTb2;
-layui.config({
-    base: baseUrl + "/static/layui/lay/modules/"
-}).use(['table','laydate', 'form', 'util', 'admin'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-    var admin = layui.admin;
-    var util = layui.util;
+(function () {
+    var simpleEntityName = 'user';
+    var entityName = 'User';
+    var primaryKeyField = 'id';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'id',
+        columnName: 'id',
+        label: 'I銆�銆�D',
+        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: 'username',
+        columnName: 'username',
+        label: '璐︺��銆�鍙�',
+        tableProp: 'username',
+        exportField: 'username',
+        kind: 'text',
+        valueType: 'string',
+        required: true,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'password',
+        columnName: 'password',
+        label: '瀵嗐��銆�鐮�',
+        tableProp: 'password',
+        exportField: 'password',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'nickname',
+        columnName: 'nickname',
+        label: '鏄点��銆�绉�',
+        tableProp: 'nickname',
+        exportField: 'nickname',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'avatar',
+        columnName: 'avatar',
+        label: '澶淬��銆�鍍�',
+        tableProp: 'avatar',
+        exportField: 'avatar',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'code',
+        columnName: 'code',
+        label: '宸ャ��銆�鍙�',
+        tableProp: 'code',
+        exportField: 'code',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sex',
+        columnName: 'sex',
+        label: '鎬с��銆�鍒�',
+        tableProp: 'sex$',
+        exportField: 'sex$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鏈煡' }, { rawValue: '1', label: '鐢�' }, { rawValue: '2', label: '濂�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'phone',
+        columnName: 'phone',
+        label: '鎵� 鏈� 鍙�',
+        tableProp: 'phone',
+        exportField: 'phone',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'email',
+        columnName: 'email',
+        label: '閭��銆�绠�',
+        tableProp: 'email',
+        exportField: 'email',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'emailVerified',
+        columnName: 'email_verified',
+        label: '閭楠岃瘉',
+        tableProp: 'emailVerified$',
+        exportField: 'emailVerified$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鍚�' }, { rawValue: '1', label: '鏄�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deptId',
+        columnName: 'dept_id',
+        label: '鎵�灞為儴闂�',
+        tableProp: 'deptId$',
+        exportField: 'deptId$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'dept',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'realName',
+        columnName: 'real_name',
+        label: '鐪熷疄濮撳悕',
+        tableProp: 'realName',
+        exportField: 'realName',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'idCard',
+        columnName: 'id_card',
+        label: '韬唤璇佸彿',
+        tableProp: 'idCard',
+        exportField: 'idCard',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'birthday',
+        columnName: 'birthday',
+        label: '鍑虹敓鏃ユ湡',
+        tableProp: 'birthday',
+        exportField: 'birthday',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'introduction',
+        columnName: 'introduction',
+        label: '涓汉绠�浠�',
+        tableProp: 'introduction',
+        exportField: 'introduction',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'hostId',
+        columnName: 'host_id',
+        label: '鎵�灞炴満鏋�',
+        tableProp: 'hostId$',
+        exportField: 'hostId$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'host',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deleted',
+        columnName: 'deleted',
+        label: '鏄惁鍒犻櫎',
+        tableProp: 'deleted$',
+        exportField: 'deleted$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '1', label: '鏄�' }, { rawValue: '0', 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: '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: 'hostId',
+        columnName: 'host_id',
+        label: '*鎺堟潈鍟嗘埛',
+        tableProp: 'hostId',
+        exportField: 'hostId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deptId',
+        columnName: 'dept_id',
+        label: '',
+        tableProp: 'deptId',
+        exportField: 'deptId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'roleId',
+        columnName: 'role_id',
+        label: '*瑙掋��銆�鑹�',
+        tableProp: 'roleId',
+        exportField: 'roleId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'username',
+        columnName: 'username',
+        label: '鐧诲綍璐︽埛',
+        tableProp: 'username',
+        exportField: 'username',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'nickname',
+        columnName: 'nickname',
+        label: '鐢ㄦ埛鍚�',
+        tableProp: 'nickname',
+        exportField: 'nickname',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'mobile',
+        columnName: 'mobile',
+        label: '鎵嬫満鍙�',
+        tableProp: 'mobile',
+        exportField: 'mobile',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'password',
+        columnName: 'password',
+        label: '瀵嗐��銆�鐮�',
+        tableProp: 'password',
+        exportField: 'password',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'avatar',
+        columnName: 'avatar',
+        label: '',
+        tableProp: 'avatar',
+        exportField: 'avatar',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'email',
+        columnName: 'email',
+        label: '閭',
+        tableProp: 'email',
+        exportField: 'email',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sex',
+        columnName: 'sex',
+        label: '鎬у埆',
+        tableProp: 'sex',
+        exportField: 'sex',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: 'hostId',
+        columnName: 'host_id',
+        label: '*鎺堟潈鍟嗘埛',
+        tableProp: 'hostId',
+        exportField: 'hostId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deptId',
+        columnName: 'dept_id',
+        label: '',
+        tableProp: 'deptId',
+        exportField: 'deptId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'roleId',
+        columnName: 'role_id',
+        label: '*瑙掋��銆�鑹�',
+        tableProp: 'roleId',
+        exportField: 'roleId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'username',
+        columnName: 'username',
+        label: '鐧诲綍璐︽埛',
+        tableProp: 'username',
+        exportField: 'username',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'nickname',
+        columnName: 'nickname',
+        label: '鐢ㄦ埛鍚�',
+        tableProp: 'nickname',
+        exportField: 'nickname',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'mobile',
+        columnName: 'mobile',
+        label: '鎵嬫満鍙�',
+        tableProp: 'mobile',
+        exportField: 'mobile',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'password',
+        columnName: 'password',
+        label: '瀵嗐��銆�鐮�',
+        tableProp: 'password',
+        exportField: 'password',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'avatar',
+        columnName: 'avatar',
+        label: '',
+        tableProp: 'avatar',
+        exportField: 'avatar',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'email',
+        columnName: 'email',
+        label: '閭',
+        tableProp: 'email',
+        exportField: 'email',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sex',
+        columnName: 'sex',
+        label: '鎬у埆',
+        tableProp: 'sex',
+        exportField: 'sex',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'id',
+        columnName: 'id',
+        label: 'I銆�銆�D',
+        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: 'username',
+        columnName: 'username',
+        label: '璐︺��銆�鍙�',
+        tableProp: 'username',
+        exportField: 'username',
+        kind: 'text',
+        valueType: 'string',
+        required: true,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'password',
+        columnName: 'password',
+        label: '瀵嗐��銆�鐮�',
+        tableProp: 'password',
+        exportField: 'password',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'nickname',
+        columnName: 'nickname',
+        label: '鏄点��銆�绉�',
+        tableProp: 'nickname',
+        exportField: 'nickname',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'avatar',
+        columnName: 'avatar',
+        label: '澶淬��銆�鍍�',
+        tableProp: 'avatar',
+        exportField: 'avatar',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'code',
+        columnName: 'code',
+        label: '宸ャ��銆�鍙�',
+        tableProp: 'code',
+        exportField: 'code',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sex',
+        columnName: 'sex',
+        label: '鎬с��銆�鍒�',
+        tableProp: 'sex$',
+        exportField: 'sex$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鏈煡' }, { rawValue: '1', label: '鐢�' }, { rawValue: '2', label: '濂�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'phone',
+        columnName: 'phone',
+        label: '鎵� 鏈� 鍙�',
+        tableProp: 'phone',
+        exportField: 'phone',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'email',
+        columnName: 'email',
+        label: '閭��銆�绠�',
+        tableProp: 'email',
+        exportField: 'email',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'emailVerified',
+        columnName: 'email_verified',
+        label: '閭楠岃瘉',
+        tableProp: 'emailVerified$',
+        exportField: 'emailVerified$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鍚�' }, { rawValue: '1', label: '鏄�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deptId',
+        columnName: 'dept_id',
+        label: '鎵�灞為儴闂�',
+        tableProp: 'deptId$',
+        exportField: 'deptId$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'dept',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'realName',
+        columnName: 'real_name',
+        label: '鐪熷疄濮撳悕',
+        tableProp: 'realName',
+        exportField: 'realName',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'idCard',
+        columnName: 'id_card',
+        label: '韬唤璇佸彿',
+        tableProp: 'idCard',
+        exportField: 'idCard',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'birthday',
+        columnName: 'birthday',
+        label: '鍑虹敓鏃ユ湡',
+        tableProp: 'birthday',
+        exportField: 'birthday',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'introduction',
+        columnName: 'introduction',
+        label: '涓汉绠�浠�',
+        tableProp: 'introduction',
+        exportField: 'introduction',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'tenantId',
+        columnName: 'tenant_id',
+        label: '鎵�灞炴満鏋�',
+        tableProp: 'tenantId$',
+        exportField: 'tenantId$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'host',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deleted',
+        columnName: 'deleted',
+        label: '鏄惁鍒犻櫎',
+        tableProp: 'deleted$',
+        exportField: 'deleted$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '1', label: '鏄�' }, { rawValue: '0', 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: '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: 'hostId',
+        columnName: 'host_id',
+        label: '*鎺堟潈鍟嗘埛',
+        tableProp: 'hostId',
+        exportField: 'hostId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deptId',
+        columnName: 'dept_id',
+        label: '',
+        tableProp: 'deptId',
+        exportField: 'deptId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'roleId',
+        columnName: 'role_id',
+        label: '*瑙掋��銆�鑹�',
+        tableProp: 'roleId',
+        exportField: 'roleId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'username',
+        columnName: 'username',
+        label: '鐧诲綍璐︽埛',
+        tableProp: 'username',
+        exportField: 'username',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'nickname',
+        columnName: 'nickname',
+        label: '鐢ㄦ埛鍚�',
+        tableProp: 'nickname',
+        exportField: 'nickname',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'mobile',
+        columnName: 'mobile',
+        label: '鎵嬫満鍙�',
+        tableProp: 'mobile',
+        exportField: 'mobile',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'password',
+        columnName: 'password',
+        label: '瀵嗐��銆�鐮�',
+        tableProp: 'password',
+        exportField: 'password',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'avatar',
+        columnName: 'avatar',
+        label: '',
+        tableProp: 'avatar',
+        exportField: 'avatar',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'email',
+        columnName: 'email',
+        label: '閭',
+        tableProp: 'email',
+        exportField: 'email',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sex',
+        columnName: 'sex',
+        label: '鎬у埆',
+        tableProp: 'sex',
+        exportField: 'sex',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: 'hostId',
+        columnName: 'host_id',
+        label: '*鎺堟潈鍟嗘埛',
+        tableProp: 'hostId',
+        exportField: 'hostId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deptId',
+        columnName: 'dept_id',
+        label: '',
+        tableProp: 'deptId',
+        exportField: 'deptId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'roleId',
+        columnName: 'role_id',
+        label: '*瑙掋��銆�鑹�',
+        tableProp: 'roleId',
+        exportField: 'roleId',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'username',
+        columnName: 'username',
+        label: '鐧诲綍璐︽埛',
+        tableProp: 'username',
+        exportField: 'username',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'nickname',
+        columnName: 'nickname',
+        label: '鐢ㄦ埛鍚�',
+        tableProp: 'nickname',
+        exportField: 'nickname',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'mobile',
+        columnName: 'mobile',
+        label: '鎵嬫満鍙�',
+        tableProp: 'mobile',
+        exportField: 'mobile',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'password',
+        columnName: 'password',
+        label: '瀵嗐��銆�鐮�',
+        tableProp: 'password',
+        exportField: 'password',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'avatar',
+        columnName: 'avatar',
+        label: '',
+        tableProp: 'avatar',
+        exportField: 'avatar',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'email',
+        columnName: 'email',
+        label: '閭',
+        tableProp: 'email',
+        exportField: 'email',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sex',
+        columnName: 'sex',
+        label: '鎬у埆',
+        tableProp: 'sex',
+        exportField: 'sex',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        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: 'status',
+        columnName: 'status',
+        label: '鐘舵��',
+        tableProp: 'status',
+        exportField: 'status',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'id',
+        columnName: 'id',
+        label: 'I銆�銆�D',
+        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: 'username',
+        columnName: 'username',
+        label: '璐︺��銆�鍙�',
+        tableProp: 'username',
+        exportField: 'username',
+        kind: 'text',
+        valueType: 'string',
+        required: true,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'password',
+        columnName: 'password',
+        label: '瀵嗐��銆�鐮�',
+        tableProp: 'password',
+        exportField: 'password',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'nickname',
+        columnName: 'nickname',
+        label: '鏄点��銆�绉�',
+        tableProp: 'nickname',
+        exportField: 'nickname',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'avatar',
+        columnName: 'avatar',
+        label: '澶淬��銆�鍍�',
+        tableProp: 'avatar',
+        exportField: 'avatar',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'code',
+        columnName: 'code',
+        label: '宸ャ��銆�鍙�',
+        tableProp: 'code',
+        exportField: 'code',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sex',
+        columnName: 'sex',
+        label: '鎬с��銆�鍒�',
+        tableProp: 'sex$',
+        exportField: 'sex$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鏈煡' }, { rawValue: '1', label: '鐢�' }, { rawValue: '2', label: '濂�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'phone',
+        columnName: 'phone',
+        label: '鎵� 鏈� 鍙�',
+        tableProp: 'phone',
+        exportField: 'phone',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'email',
+        columnName: 'email',
+        label: '閭��銆�绠�',
+        tableProp: 'email',
+        exportField: 'email',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'emailVerified',
+        columnName: 'email_verified',
+        label: '閭楠岃瘉',
+        tableProp: 'emailVerified$',
+        exportField: 'emailVerified$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鍚�' }, { rawValue: '1', label: '鏄�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deptId',
+        columnName: 'dept_id',
+        label: '鎵�灞為儴闂�',
+        tableProp: 'deptId$',
+        exportField: 'deptId$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'dept',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'realName',
+        columnName: 'real_name',
+        label: '鐪熷疄濮撳悕',
+        tableProp: 'realName',
+        exportField: 'realName',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'idCard',
+        columnName: 'id_card',
+        label: '韬唤璇佸彿',
+        tableProp: 'idCard',
+        exportField: 'idCard',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'birthday',
+        columnName: 'birthday',
+        label: '鍑虹敓鏃ユ湡',
+        tableProp: 'birthday',
+        exportField: 'birthday',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'introduction',
+        columnName: 'introduction',
+        label: '涓汉绠�浠�',
+        tableProp: 'introduction',
+        exportField: 'introduction',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'hostId',
+        columnName: 'host_id',
+        label: '鎵�灞炴満鏋�',
+        tableProp: 'hostId$',
+        exportField: 'hostId$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'host',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deleted',
+        columnName: 'deleted',
+        label: '鏄惁鍒犻櫎',
+        tableProp: 'deleted$',
+        exportField: 'deleted$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '1', label: '鏄�' }, { rawValue: '0', 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: 'id',
+        columnName: 'id',
+        label: 'I銆�銆�D',
+        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: 'username',
+        columnName: 'username',
+        label: '璐︺��銆�鍙�',
+        tableProp: 'username',
+        exportField: 'username',
+        kind: 'text',
+        valueType: 'string',
+        required: true,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'password',
+        columnName: 'password',
+        label: '瀵嗐��銆�鐮�',
+        tableProp: 'password',
+        exportField: 'password',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'nickname',
+        columnName: 'nickname',
+        label: '鏄点��銆�绉�',
+        tableProp: 'nickname',
+        exportField: 'nickname',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'avatar',
+        columnName: 'avatar',
+        label: '澶淬��銆�鍍�',
+        tableProp: 'avatar',
+        exportField: 'avatar',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'code',
+        columnName: 'code',
+        label: '宸ャ��銆�鍙�',
+        tableProp: 'code',
+        exportField: 'code',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sex',
+        columnName: 'sex',
+        label: '鎬с��銆�鍒�',
+        tableProp: 'sex$',
+        exportField: 'sex$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鏈煡' }, { rawValue: '1', label: '鐢�' }, { rawValue: '2', label: '濂�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'phone',
+        columnName: 'phone',
+        label: '鎵� 鏈� 鍙�',
+        tableProp: 'phone',
+        exportField: 'phone',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 116,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'email',
+        columnName: 'email',
+        label: '閭��銆�绠�',
+        tableProp: 'email',
+        exportField: 'email',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'emailVerified',
+        columnName: 'email_verified',
+        label: '閭楠岃瘉',
+        tableProp: 'emailVerified$',
+        exportField: 'emailVerified$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '0', label: '鍚�' }, { rawValue: '1', label: '鏄�' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deptId',
+        columnName: 'dept_id',
+        label: '鎵�灞為儴闂�',
+        tableProp: 'deptId$',
+        exportField: 'deptId$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'dept',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'realName',
+        columnName: 'real_name',
+        label: '鐪熷疄濮撳悕',
+        tableProp: 'realName',
+        exportField: 'realName',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'idCard',
+        columnName: 'id_card',
+        label: '韬唤璇佸彿',
+        tableProp: 'idCard',
+        exportField: 'idCard',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'birthday',
+        columnName: 'birthday',
+        label: '鍑虹敓鏃ユ湡',
+        tableProp: 'birthday',
+        exportField: 'birthday',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'introduction',
+        columnName: 'introduction',
+        label: '涓汉绠�浠�',
+        tableProp: 'introduction',
+        exportField: 'introduction',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'hostId',
+        columnName: 'host_id',
+        label: '鎵�灞炴満鏋�',
+        tableProp: 'hostId$',
+        exportField: 'hostId$',
+        kind: 'foreign',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: 'host',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        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: '姝e父' }, { rawValue: '0', label: '绂佺敤' }],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'deleted',
+        columnName: 'deleted',
+        label: '鏄惁鍒犻櫎',
+        tableProp: 'deleted$',
+        exportField: 'deleted$',
+        kind: 'enum',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 120,
+        enumOptions: [{ rawValue: '1', label: '鏄�' }, { rawValue: '0', 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'
+    }
 
-    insTb2 = table.render({
-        elem: '#userTable',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/user/list/auth',
-        page: true,
-        limit: 15,
-        limits: [15, 30, 50, 100, 200, 500],
-        toolbar: '#userToolbar',
-        height: 'full-100',
-        cols: [[
-            {type: 'checkbox'}
-            // ,{field: 'hostName', align: 'center',title: '鎺堟潈鍟嗘埛', templet: '#hostTpl', width: 140}
-            // ,{field: 'nickname', align: 'center',title: '鐢ㄦ埛鍚�'}
-            ,{field: 'username', align: 'center',title: '鐧诲綍璐︽埛'}
-            ,{field: 'mobile', align: 'center',title: '鎵嬫満鍙�'}
-            // ,{field: 'deptName', align: 'center',title: '鎵�灞為儴闂�'}
-            ,{field: 'roleName', align: 'center',title: '瑙掕壊'}
-            ,{field: 'email', align: 'center',title: '閭'}
-            // ,{field: 'sex$', align: 'center',title: '鎬у埆'}
-            ,{field: 'createTime$', align: 'center',title: '娉ㄥ唽鏃堕棿', hide: true}
-            ,{field: 'status$', align: 'center',title: '鐘舵��', templet: '#statusTpl', width: 120, unresize: true}
+    ]);
 
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:170}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            tableData = table.cache.userTable;
-            pageCurr=curr;
-            limit();
+    function formatFieldLabel(field) {
+        var raw = field && field.label ? String(field.label).trim() : '';
+        if (raw) {
+            return raw;
         }
-    });
-    /* 琛ㄦ牸2鎼滅储 */
-    form.on('submit(userTbSearch)', function (data) {
-        insTb2.reload({where: data.field, page: {curr: 1}});
-        return false;
-    });
+        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();
+        });
+    }
 
-    /* 琛ㄦ牸2澶村伐鍏锋爮鐐瑰嚮浜嬩欢 */
-    table.on('toolbar(userTable)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id).data;
-        if (obj.event === 'add') { // 娣诲姞
-            showEditModel()
-        } else if (obj.event === 'del') { // 鍒犻櫎
-            if (checkStatus.length === 0) {
-                layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
+    function dedupeFieldMeta(list) {
+        var result = [];
+        var seen = {};
+        (list || []).forEach(function (field) {
+            if (!field || !field.field || seen[field.field]) {
                 return;
             }
-            del(checkStatus.map(function (d) {
-                return d.id;
-            }));
+            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;
         }
-    });
-
-    // 淇敼鐘舵��
-    form.on('switch(statusSwitch)', function (obj) {
-        var index  = obj.othis.parents('tr').attr("data-index");
-        var data = tableData[index];
-        data[this.name] = obj.elem.checked?1:0;
-        http.post(baseUrl+"/user/edit/auth", {id: data.id, status: data[this.name]}, function (res) {
-            layer.msg(res.msg, {icon: 1});
-        })
-    })
-
-    /* 琛ㄦ牸2宸ュ叿鏉$偣鍑讳簨浠� */
-    table.on('tool(userTable)', function (obj) {
-        var data = obj.data;
-        switch (obj.event) {
-            // 缂栬緫
-            case 'edit':
-                showEditModel(data)
-                break;
-            // 鍒犻櫎
-            case 'del':
-                del([data.id]);
-                break;
-            // 閲嶇疆瀵嗙爜
-            case 'resetPwd':
-                admin.open({
-                    type: 1,
-                    title: '閲嶇疆瀵嗙爜',
-                    offset: '150px',
-                    area: ['360px'],
-                    shade: 0.1,
-                    shadeClose: true,
-                    content: $("#resetpwd-window").html(),
-                    success: function(layero, index){
-                        layer.iframeAuto(index);
-                        $('#resetUserId').val(data.id);
-                    }
-                });
-                break;
+        if (rawValue === '') {
+            return '';
         }
-    });
+        if (field && field.valueType === 'number') {
+            var numberVal = Number(rawValue);
+            return isNaN(numberVal) ? rawValue : numberVal;
+        }
+        return String(rawValue);
+    }
 
-    /* 鏄剧ず琛ㄥ崟寮圭獥 */
-    function showEditModel(mData) {
-        admin.open({
-            type: 1,
-            area: '600px',
-            title: (mData ? '淇敼' : '娣诲姞') + '鐢ㄦ埛',
-            content: $('#editDialog').html(),
-            success: function (layero, dIndex) {
-                // 鍥炴樉琛ㄥ崟鏁版嵁
-                form.val('detail', mData);
-                // 琛ㄥ崟鎻愪氦浜嬩欢
-                form.on('submit(editSubmit)', function (data) {
-                    if (isEmpty(data.field.roleId)) {
-                        layer.msg('璇烽�夋嫨瑙掕壊', {icon: 2});
-                        return false;
-                    }
-                    var loadIndex = layer.load(2);
-                    $.ajax({
-                        url: baseUrl+"/user/"+(mData?'update':'add')+"/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: data.field,
-                        method: 'POST',
-                        success: function (res) {
-                            layer.close(loadIndex);
-                            if (res.code === 200){
-                                layer.close(dIndex);
-                                layer.msg(res.msg, {icon: 1});
-                                $(".layui-laypage-btn")[0].click();
-                            } else if (res.code === 403){
-                                top.location.href = baseUrl+"/";
-                            }else {
-                                layer.msg(res.msg, {icon: 2});
-                            }
-                        }
-                    })
-                    return false;
-                });
-                // 寮圭獥涓嶅嚭鐜版粴鍔ㄦ潯
-                $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                layui.form.render('select');
+    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 del(ids) {
-        layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-            skin: 'layui-layer-admin',
-            shade: .1
-        }, function (i) {
-            layer.close(i);
-            var loadIndex = layer.load(2);
+    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+"/user/delete/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: {ids: ids},
-                method: 'POST',
+                url: baseUrl + '/' + field.foreignQuery + 'Query/auth',
+                method: 'GET',
+                headers: self.authHeaders(),
+                data: { condition: queryString },
                 success: function (res) {
-                    layer.close(loadIndex);
-                    if (res.code === 200){
-                        layer.msg(res.msg, {icon: 1});
-                        $(".layui-laypage-btn")[0].click();
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    }else {
-                        layer.msg(res.msg, {icon: 2});
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
             })
-
         });
     }
 
-    // 閲嶇疆瀵嗙爜
-    form.on('submit(savePwd)', function (data) {
-        $.ajax({
-            url: baseUrl+"/user/update/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: {
-                id: data.field.resetUserId,
-                password: hex_md5(data.field.resetPassword)
-            },
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    layer.closeAll();
-                    layer.msg("閲嶇疆瀵嗙爜鎴愬姛", {icon: 1});
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg, {icon: 2})
-                }
-            }
-        })
-        return false;
-    })
-
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
-        }
-    });
-}
-
+})();
diff --git a/src/main/webapp/static/js/userLogin/userLogin.js b/src/main/webapp/static/js/userLogin/userLogin.js
index f0907ef..3f90a84 100644
--- a/src/main/webapp/static/js/userLogin/userLogin.js
+++ b/src/main/webapp/static/js/userLogin/userLogin.js
@@ -1,369 +1,468 @@
-var pageCurr;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
-
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#userLogin',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/userLogin/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {type: 'checkbox', fixed: 'left'}
-            ,{field: 'id', title: 'ID', sort: true,align: 'center', fixed: 'left', width: 80}
-            ,{field: 'userUsername', align: 'center',title: '鍛樺伐',event: 'User', style: 'text-decoration: underline;cursor:pointer'}
-            ,{field: 'token', align: 'center',title: '鍑瘉鍊�'}
-            ,{field: 'createTime$', align: 'center',title: '娣诲姞鏃堕棿'}
-
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:150}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
-            }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            limit();
-        }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(userLogin)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
-            }
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(userLogin)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'userLogin_detail.html',
-                    success: function(layero, index){
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                var ids=[];
-                data.map(function (track) {
-                    ids.push(track.id);
-                });
-                if (ids.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(ids.length===1?'姝�':ids.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/userLogin/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {ids: ids},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function() {
-                    var titles = [];
-                    var fields = [];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function () {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'userLogin': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/userLogin/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType: 'json',
-                        contentType: 'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles, res.data, 'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
-        }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(userLogin)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'userLogin_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'userLogin_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                    }
-                });
-                break;
-            case 'User':
-                var param = top.reObject(data).userId;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                    layer.open({
-                        type: 2,
-                        title: '璇︽儏',
-                        maxmin: true,
-                        area: [top.detailHeight, top.detailWidth],
-                        shadeClose: false,
-                        content: '../user/user_detail.html',
-                        success: function(layero, index){
-                            $.ajax({
-                                url: baseUrl+"/user/"+ param +"/auth",
-                                headers: {'token': localStorage.getItem('token')},
-                                method: 'GET',
-                                success: function (res) {
-                                    if (res.code === 200){
-                                        setFormVal(layer.getChildFrame('#detail', index), res.data);
-                                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                        layer.getChildFrame('#data-detail-submit,#prompt', index).hide();
-                                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                    } else if (res.code === 403){
-                                        parent.location.href = "/";
-                                    }else {
-                                        layer.msg(res.msg)
-                                    }
-                                }
-                            })
-                        }
-                    });
-                }
-                break;
-
-        }
-    });
-
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-            id: $('#id').val(),
-            userId: $('#userId').val(),
-            token: $('#token').val(),
-            createTime: top.strToDate($('#createTime\\$').val()),
-
+(function () {
+    function authHeaders() {
+        return {
+            token: localStorage.getItem('token') || ''
         };
-        $.ajax({
-            url: baseUrl+"/userLogin/edit/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    tableReload(true);
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
+    }
+
+    function isForbidden(res) {
+        return res && Number(res.code) === 403;
+    }
+
+    function isOk(res) {
+        return res && Number(res.code) === 200;
+    }
+
+    function normalizeId(value) {
+        if (value === null || value === undefined || value === '') {
+            return null;
+        }
+        var numberValue = Number(value);
+        return isNaN(numberValue) ? value : numberValue;
+    }
+
+    function createLoginForm() {
+        return {
+            id: null,
+            userId: null,
+            token: '',
+            createTime: ''
+        };
+    }
+
+    function escapeHtml(value) {
+        return String(value == null ? '' : value)
+            .replace(/&/g, '&amp;')
+            .replace(/</g, '&lt;')
+            .replace(/>/g, '&gt;')
+            .replace(/"/g, '&quot;')
+            .replace(/'/g, '&#39;');
+    }
+
+    function downloadExcel(filename, headers, rows) {
+        var headHtml = headers.map(function (title) {
+            return '<th style="mso-number-format:\\@;">' + escapeHtml(title) + '</th>';
+        }).join('');
+        var bodyHtml = rows.map(function (row) {
+            return '<tr>' + row.map(function (cell) {
+                return '<td style="mso-number-format:\\@;">' + escapeHtml(cell) + '</td>';
+            }).join('') + '</tr>';
+        }).join('');
+        var workbook = [
+            '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">',
+            '<head><meta charset="UTF-8"></head>',
+            '<body><table border="1"><tr>',
+            headHtml,
+            '</tr>',
+            bodyHtml,
+            '</table></body></html>'
+        ].join('');
+        var blob = new Blob([workbook], {type: 'application/vnd.ms-excel;charset=utf-8;'});
+        var link = document.createElement('a');
+        link.href = URL.createObjectURL(blob);
+        link.download = filename;
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        URL.revokeObjectURL(link.href);
+    }
+
+    new Vue({
+        el: '#app',
+        data: function () {
+            return {
+                loading: false,
+                exportLoading: false,
+                userSearchLoading: false,
+                dialogUserLoading: false,
+                tableHeight: Math.max(window.innerHeight - 220, 360),
+                tableData: [],
+                selection: [],
+                pagination: {
+                    curr: 1,
+                    limit: 16,
+                    total: 0
+                },
+                searchForm: {
+                    userId: null
+                },
+                userSearchOptions: [],
+                loginDialog: {
+                    visible: false,
+                    mode: 'create',
+                    readonly: false,
+                    submitting: false
+                },
+                loginForm: createLoginForm(),
+                loginRules: {
+                    userId: [
+                        {required: true, message: '璇烽�夋嫨鍛樺伐', trigger: 'change'}
+                    ],
+                    token: [
+                        {required: true, message: '璇疯緭鍏ュ嚟璇佸��', trigger: 'blur'}
+                    ],
+                    createTime: [
+                        {required: true, message: '璇烽�夋嫨娣诲姞鏃堕棿', trigger: 'change'}
+                    ]
+                },
+                dialogUserOptions: [],
+                userDetail: {
+                    visible: false,
+                    data: {}
                 }
-                layer.close(index);
-            }
-        })
-    });
-
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    layDate.render({
-        elem: '#createTime\\$',
-        type: 'datetime'
-    });
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
+            };
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
-            }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
-                    page: {
-                        curr: pageCurr-1
+        created: function () {
+            this.loadList();
+        },
+        mounted: function () {
+            window.addEventListener('resize', this.handleResize);
+        },
+        beforeDestroy: function () {
+            window.removeEventListener('resize', this.handleResize);
+        },
+        methods: {
+            handleResize: function () {
+                this.tableHeight = Math.max(window.innerHeight - 220, 360);
+                this.refreshTableLayout();
+            },
+            handleForbidden: function (res) {
+                if (isForbidden(res)) {
+                    top.location.href = baseUrl + '/';
+                    return true;
+                }
+                return false;
+            },
+            refreshTableLayout: function () {
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.dataTable && vm.$refs.dataTable.doLayout) {
+                        vm.$refs.dataTable.doLayout();
                     }
                 });
-                pageCurr -= 1;
+            },
+            currentQuery: function () {
+                return {
+                    curr: this.pagination.curr,
+                    limit: this.pagination.limit,
+                    userId: this.searchForm.userId
+                };
+            },
+            loadList: function () {
+                var vm = this;
+                vm.loading = true;
+                $.ajax({
+                    url: baseUrl + '/userLogin/list/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    data: vm.currentQuery(),
+                    success: function (res) {
+                        vm.loading = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '鍑瘉鍔犺浇澶辫触');
+                            return;
+                        }
+                        var data = res.data || {};
+                        vm.tableData = data.records || [];
+                        vm.pagination.total = data.total || 0;
+                        vm.selection = [];
+                        vm.refreshTableLayout();
+                    },
+                    error: function () {
+                        vm.loading = false;
+                        vm.$message.error('鍑瘉鍔犺浇澶辫触');
+                    }
+                });
+            },
+            fetchUserOptions: function (keyword, target) {
+                var vm = this;
+                var loadingKey = target === 'search' ? 'userSearchLoading' : 'dialogUserLoading';
+                vm[loadingKey] = true;
+                $.ajax({
+                    url: baseUrl + '/userQuery/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    data: {
+                        condition: keyword || ''
+                    },
+                    success: function (res) {
+                        vm[loadingKey] = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '鐢ㄦ埛鏌ヨ澶辫触');
+                            return;
+                        }
+                        if (target === 'search') {
+                            vm.userSearchOptions = res.data || [];
+                        } else {
+                            vm.dialogUserOptions = res.data || [];
+                        }
+                    },
+                    error: function () {
+                        vm[loadingKey] = false;
+                        vm.$message.error('鐢ㄦ埛鏌ヨ澶辫触');
+                    }
+                });
+            },
+            searchUserOptions: function (keyword) {
+                this.fetchUserOptions(keyword, 'search');
+            },
+            searchDialogUserOptions: function (keyword) {
+                this.fetchUserOptions(keyword, 'dialog');
+            },
+            ensureDialogUserOption: function (id, label) {
+                if (!id || !label) {
+                    return;
+                }
+                var exists = this.dialogUserOptions.some(function (item) {
+                    return normalizeId(item.id) === normalizeId(id);
+                });
+                if (!exists) {
+                    this.dialogUserOptions = [{
+                        id: normalizeId(id),
+                        value: label
+                    }].concat(this.dialogUserOptions);
+                }
+            },
+            handleSelectionChange: function (rows) {
+                this.selection = rows || [];
+            },
+            handleSearch: function () {
+                this.pagination.curr = 1;
+                this.loadList();
+            },
+            handleReset: function () {
+                this.searchForm.userId = null;
+                this.userSearchOptions = [];
+                this.pagination.curr = 1;
+                this.loadList();
+            },
+            handleCurrentChange: function (page) {
+                this.pagination.curr = page;
+                this.loadList();
+            },
+            handleSizeChange: function (size) {
+                this.pagination.limit = size;
+                this.pagination.curr = 1;
+                this.loadList();
+            },
+            resetLoginDialog: function () {
+                this.loginForm = createLoginForm();
+                this.dialogUserOptions = [];
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.loginForm) {
+                        vm.$refs.loginForm.clearValidate();
+                    }
+                });
+            },
+            openCreateDialog: function () {
+                this.loginDialog.mode = 'create';
+                this.loginDialog.readonly = false;
+                this.loginDialog.visible = true;
+                this.resetLoginDialog();
+            },
+            openEditDialog: function (row) {
+                this.loginDialog.mode = 'edit';
+                this.loginDialog.readonly = false;
+                this.loginDialog.visible = true;
+                this.loginForm = {
+                    id: normalizeId(row.id),
+                    userId: normalizeId(row.userId),
+                    token: row.token || '',
+                    createTime: row['createTime$'] || ''
+                };
+                this.dialogUserOptions = [];
+                this.ensureDialogUserOption(row.userId, row.userUsername);
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.loginForm) {
+                        vm.$refs.loginForm.clearValidate();
+                    }
+                });
+            },
+            openDetailDialog: function (row) {
+                this.loginDialog.mode = 'edit';
+                this.loginDialog.readonly = true;
+                this.loginDialog.visible = true;
+                this.loginForm = {
+                    id: normalizeId(row.id),
+                    userId: normalizeId(row.userId),
+                    token: row.token || '',
+                    createTime: row['createTime$'] || ''
+                };
+                this.dialogUserOptions = [];
+                this.ensureDialogUserOption(row.userId, row.userUsername);
+                var vm = this;
+                this.$nextTick(function () {
+                    if (vm.$refs.loginForm) {
+                        vm.$refs.loginForm.clearValidate();
+                    }
+                });
+            },
+            submitLogin: function () {
+                var vm = this;
+                if (!vm.$refs.loginForm) {
+                    return;
+                }
+                vm.$refs.loginForm.validate(function (valid) {
+                    if (!valid) {
+                        return false;
+                    }
+                    vm.loginDialog.submitting = true;
+                    $.ajax({
+                        url: baseUrl + '/userLogin/' + (vm.loginDialog.mode === 'create' ? 'add' : 'update') + '/auth',
+                        method: 'POST',
+                        headers: authHeaders(),
+                        data: {
+                            id: vm.loginDialog.mode === 'edit' ? normalizeId(vm.loginForm.id) : null,
+                            userId: normalizeId(vm.loginForm.userId),
+                            token: $.trim(vm.loginForm.token),
+                            createTime: vm.loginForm.createTime
+                        },
+                        success: function (res) {
+                            vm.loginDialog.submitting = false;
+                            if (vm.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!isOk(res)) {
+                                vm.$message.error(res && res.msg ? res.msg : '淇濆瓨澶辫触');
+                                return;
+                            }
+                            vm.$message.success(res.msg || '淇濆瓨鎴愬姛');
+                            vm.loginDialog.visible = false;
+                            vm.loadList();
+                        },
+                        error: function () {
+                            vm.loginDialog.submitting = false;
+                            vm.$message.error('淇濆瓨澶辫触');
+                        }
+                    });
+                    return true;
+                });
+            },
+            removeSelection: function () {
+                var vm = this;
+                if (!vm.selection.length) {
+                    vm.$message.warning('璇烽�夋嫨鏁版嵁');
+                    return;
+                }
+                var ids = vm.selection.map(function (row) {
+                    return normalizeId(row.id);
+                }).filter(function (id) {
+                    return id !== null;
+                });
+                vm.$confirm('纭畾鍒犻櫎閫変腑鍑瘉鍚楋紵', '鎻愮ず', {
+                    confirmButtonText: '纭畾',
+                    cancelButtonText: '鍙栨秷',
+                    type: 'warning'
+                }).then(function () {
+                    $.ajax({
+                        url: baseUrl + '/userLogin/delete/auth',
+                        method: 'POST',
+                        headers: authHeaders(),
+                        traditional: true,
+                        data: {
+                            ids: ids
+                        },
+                        success: function (res) {
+                            if (vm.handleForbidden(res)) {
+                                return;
+                            }
+                            if (!isOk(res)) {
+                                vm.$message.error(res && res.msg ? res.msg : '鍒犻櫎澶辫触');
+                                return;
+                            }
+                            vm.$message.success(res.msg || '鍒犻櫎鎴愬姛');
+                            if (vm.tableData.length === ids.length && vm.pagination.curr > 1) {
+                                vm.pagination.curr -= 1;
+                            }
+                            vm.loadList();
+                        },
+                        error: function () {
+                            vm.$message.error('鍒犻櫎澶辫触');
+                        }
+                    });
+                }).catch(function () {
+                });
+            },
+            exportRows: function () {
+                var vm = this;
+                vm.exportLoading = true;
+                $.ajax({
+                    url: baseUrl + '/userLogin/export/auth',
+                    method: 'POST',
+                    headers: authHeaders(),
+                    dataType: 'json',
+                    contentType: 'application/json;charset=UTF-8',
+                    data: JSON.stringify({
+                        userLogin: {
+                            userId: vm.searchForm.userId
+                        },
+                        fields: ['id', 'userUsername', 'token', 'createTime$']
+                    }),
+                    success: function (res) {
+                        vm.exportLoading = false;
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '瀵煎嚭澶辫触');
+                            return;
+                        }
+                        downloadExcel('鍑瘉璁板綍.xls', ['ID', '鍛樺伐', '鍑瘉鍊�', '娣诲姞鏃堕棿'], res.data || []);
+                    },
+                    error: function () {
+                        vm.exportLoading = false;
+                        vm.$message.error('瀵煎嚭澶辫触');
+                    }
+                });
+            },
+            openUserDetail: function (row) {
+                var vm = this;
+                if (!row.userId) {
+                    vm.$message.warning('鏃犵敤鎴锋暟鎹�');
+                    return;
+                }
+                $.ajax({
+                    url: baseUrl + '/user/' + row.userId + '/auth',
+                    method: 'GET',
+                    headers: authHeaders(),
+                    success: function (res) {
+                        if (vm.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!isOk(res)) {
+                            vm.$message.error(res && res.msg ? res.msg : '鍛樺伐璇︽儏鍔犺浇澶辫触');
+                            return;
+                        }
+                        vm.userDetail.data = res.data || {};
+                        vm.userDetail.visible = true;
+                    },
+                    error: function () {
+                        vm.$message.error('鍛樺伐璇︽儏鍔犺浇澶辫触');
+                    }
+                });
             }
-            limit(child);
+        },
+        watch: {
+            'loginDialog.visible': function (visible) {
+                if (!visible) {
+                    this.loginDialog.submitting = false;
+                    this.loginDialog.readonly = false;
+                    this.resetLoginDialog();
+                }
+            },
+            'userDetail.visible': function (visible) {
+                if (!visible) {
+                    this.userDetail.data = {};
+                }
+            }
         }
     });
-}
-
-function setFormVal(el, data) {
-    for (var val in data) {
-        el.find(":input[id='" + val + "']").val(data[val]);
-    }
-}
-
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.9);
-    }
-    layer.style(index, {
-        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
-    });
-    $(".layui-layer-shade").remove();
-}
-
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/wrkLastno/wrkLastno.js b/src/main/webapp/static/js/wrkLastno/wrkLastno.js
index 97e1769..1673b6b 100644
--- a/src/main/webapp/static/js/wrkLastno/wrkLastno.js
+++ b/src/main/webapp/static/js/wrkLastno/wrkLastno.js
@@ -1,475 +1,1273 @@
-var pageCurr;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
+(function () {
+    var simpleEntityName = 'wrkLastno';
+    var entityName = 'WrkLastno';
+    var primaryKeyField = 'wrkMk';
+    var fieldMeta = dedupeFieldMeta([
+    {
+        field: 'wrkMk',
+        columnName: 'wrk_mk',
+        label: '绫诲瀷',
+        tableProp: 'wrkMk',
+        exportField: 'wrkMk',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wrkNo',
+        columnName: 'wrk_no',
+        label: '褰撳墠ID',
+        tableProp: 'wrkNo',
+        exportField: 'wrkNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sNo',
+        columnName: 's_no',
+        label: '',
+        tableProp: 'sNo',
+        exportField: 'sNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'eNo',
+        columnName: 'e_no',
+        label: '',
+        tableProp: 'eNo',
+        exportField: 'eNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'memoM',
+        columnName: 'memo_m',
+        label: '澶囨敞',
+        tableProp: 'memoM',
+        exportField: 'memoM',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'wrkMk',
+        columnName: 'wrk_mk',
+        label: '绫诲瀷',
+        tableProp: 'wrkMk',
+        exportField: 'wrkMk',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wrkNo',
+        columnName: 'wrk_no',
+        label: '褰撳墠ID',
+        tableProp: 'wrkNo',
+        exportField: 'wrkNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sNo',
+        columnName: 's_no',
+        label: '',
+        tableProp: 'sNo',
+        exportField: 'sNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'eNo',
+        columnName: 'e_no',
+        label: '',
+        tableProp: 'eNo',
+        exportField: 'eNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'memoM',
+        columnName: 'memo_m',
+        label: '澶囨敞',
+        tableProp: 'memoM',
+        exportField: 'memoM',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'wrkMk',
+        columnName: 'wrk_mk',
+        label: '绫诲瀷',
+        tableProp: 'wrkMk',
+        exportField: 'wrkMk',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wrkNo',
+        columnName: 'wrk_no',
+        label: '褰撳墠ID',
+        tableProp: 'wrkNo',
+        exportField: 'wrkNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'sNo',
+        columnName: 's_no',
+        label: '',
+        tableProp: 'sNo',
+        exportField: 'sNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'eNo',
+        columnName: 'e_no',
+        label: '',
+        tableProp: 'eNo',
+        exportField: 'eNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'memoM',
+        columnName: 'memo_m',
+        label: '澶囨敞',
+        tableProp: 'memoM',
+        exportField: 'memoM',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#wrkLastno',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/wrkLastno/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        even: true,
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {type: 'checkbox', fixed: 'left'}
-            ,{field: 'wrkMk', align: 'center',title: '绫诲瀷'}
-            ,{field: 'sno', align: 'center',title: '璧峰ID'}
-            ,{field: 'wrkNo', align: 'center',title: '褰撳墠ID', style: 'color: #AA3130;font-weight: bold'}
-            ,{field: 'eno', align: 'center',title: '缁堟ID'}
-            ,{field: 'memoM', align: 'center',title: '澶囨敞'}
-            ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳'}
-            ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿'}
-            ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate'}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
+    ]);
+
+    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;
             }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+            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;
             }
-            pageCurr=curr;
-            limit();
-            form.on('checkbox(tableCheckbox)', function (data) {
-                var _index = $(data.elem).attr('table-index')||0;
-                if(data.elem.checked){
-                    res.data[_index][data.value] = 'Y';
-                }else{
-                    res.data[_index][data.value] = 'N';
+            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([]);
                 }
             });
-        }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(wrkLastno)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
-            }
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(wrkLastno)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'wrkLastno_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                if (data.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(data.length===1?'姝�':data.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/wrkLastno/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {param: JSON.stringify(data)},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'wrkLastno': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/wrkLastno/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
-        }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(wrkLastno)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'wrkLastno_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layer.getChildFrame('#data-detail-submit-save,#data-detail-submit-edit,#prompt', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                        layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                    }
-                });
-                break;
-            // 缂栬緫
-            case 'edit':
-                layer.open({
-                    type: 2,
-                    title: '淇敼',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'wrkLastno_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-save', index).hide();
-                        setFormVal(layer.getChildFrame('#detail', index), data, false);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), false);
-                        top.convertDisabled(layer.getChildFrame('#wrkMk', index), true);
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                        layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                    }
-                });
-                break;
-            case 'modiUser':
-                var param = top.reObject(data).modiUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '淇敼璇︽儏',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#password,#createTime\\$,#status', index).parent().parent().hide();
-                                       layer.getChildFrame('#data-detail-submit-save,#data-detail-submit-edit,#prompt', index).hide();
-                                       layer.getChildFrame('##dealDownLine', index).hide();layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-            case 'appeUser':
-                var param = top.reObject(data).appeUser;
-                if (param === undefined) {
-                    layer.msg("鏃犳暟鎹�");
-                } else {
-                   layer.open({
-                       type: 2,
-                       title: '鍒涜鎯�',
-                       maxmin: true,
-                       area: [top.detailWidth, top.detailHeight],
-                       shadeClose: false,
-                       content: '../user/user_detail.html',
-                       success: function(layero, index){
-                           $.ajax({
-                               url: baseUrl+"/user/"+ param +"/auth",
-                               headers: {'token': localStorage.getItem('token')},
-                               method: 'GET',
-                               success: function (res) {
-                                   if (res.code === 200){
-                                       setFormVal(layer.getChildFrame('#detail', index), res.data, true);
-                                       top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                                       layer.getChildFrame('#data-detail-submit-save,#data-detail-submit-edit,#prompt', index).hide();
-                                       layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                                       layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                                   } else if (res.code === 403){
-                                       parent.location.href = "/";
-                                   }else {
-                                       layer.msg(res.msg)
-                                   }
-                               }
-                           })
-                       }
-                   });
-                }
-                break;
-
-        }
-    });
-
-    // 鏁版嵁淇濆瓨鍔ㄤ綔
-    form.on('submit(save)', function () {
-        if (banMsg != null){
-            layer.msg(banMsg);
-            return;
-        }
-        method("add");
-    });
-
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        method("update")
-    });
-
-    function method(name){
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-//            id: $('#id').val(),
-            wrkMk: $('#wrkMk').val(),
-            wrkNo: $('#wrkNo').val(),
-            modiUser: $('#modiUser').val(),
-            modiTime: top.strToDate($('#modiTime\\$').val()),
-            appeUser: $('#appeUser').val(),
-            appeTime: top.strToDate($('#appeTime\\$').val()),
-            sNo: $('#sno').val(),
-            eNo: $('#eno').val(),
-            memoM: $('#memoM').val(),
-
-        };
-        $.ajax({
-            url: baseUrl+"/wrkLastno/"+name+"/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    parent.$(".layui-laypage-btn")[0].click();
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
-                }
-                layer.close(index);
-            }
-        })
-    }
-
-    // 澶嶉�夋浜嬩欢
-    form.on('checkbox(detailCheckbox)', function (data) {
-        var el = data.elem;
-        if (el.checked) {
-            $(el).val('Y');
-        } else {
-            $(el).val('N');
-        }
-    });
-
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    layDate.render({
-        elem: '#modiTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#appeTime\\$',
-        type: 'datetime'
-    });
-
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+        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;
             }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
+            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: pageCurr-1
+                        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);
                     }
-                });
-                pageCurr -= 1;
-            }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        if (find[0]!=null){
-            if (find[0].type === 'checkbox'){
-                if (data[val]==='Y'){
-                    find.attr("checked","checked");
-                    find.val('Y');
-                } else {
-                    find.remove("checked");
-                    find.val('N');
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
-                continue;
-            }
-        }
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
-                }
-            }
-        }
+            })
+        });
     }
-}
 
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.8);
-    }
-    layer.style(index, {
-//        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
-    });
-}
-
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js b/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js
index 073329a..b6acfcf 100644
--- a/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js
+++ b/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js
@@ -1,505 +1,2083 @@
-var pageCurr;
-var wrkNo;
-var ioTime;
-layui.use(['table','laydate', 'form'], function(){
-    var table = layui.table;
-    var $ = layui.jquery;
-    var layer = layui.layer;
-    var layDate = layui.laydate;
-    var form = layui.form;
+(function () {
+    var simpleEntityName = 'wrkMastLog';
+    var entityName = 'WrkMastLog';
+    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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'mk',
+        columnName: 'mk',
+        label: '',
+        tableProp: 'mk',
+        exportField: 'mk',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'wrkSts',
+        columnName: 'wrk_sts',
+        label: '宸ヤ綔鐘舵��',
+        tableProp: 'wrkSts',
+        exportField: 'wrkSts',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioType',
+        columnName: 'io_type',
+        label: '鍏ュ嚭搴撶被鍨�',
+        tableProp: 'ioType',
+        exportField: 'ioType',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioPri',
+        columnName: 'io_pri',
+        label: '浼樺厛绾�',
+        tableProp: 'ioPri',
+        exportField: 'ioPri',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: 110,
+        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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'ioTime',
+        columnName: 'io_time',
+        label: '宸ヤ綔鏃堕棿',
+        tableProp: 'ioTime$',
+        exportField: 'ioTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '宸ヤ綔鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorTime',
+        columnName: 'error_time',
+        label: '',
+        tableProp: 'errorTime$',
+        exportField: 'errorTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorMemo',
+        columnName: 'error_memo',
+        label: '',
+        tableProp: 'errorMemo',
+        exportField: 'errorMemo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: '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: 'shuttleNo',
+        columnName: 'shuttle_no',
+        label: '',
+        tableProp: 'shuttleNo',
+        exportField: 'shuttleNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'liftNo',
+        columnName: 'lift_no',
+        label: '',
+        tableProp: 'liftNo',
+        exportField: 'liftNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wmsWrkNo',
+        columnName: 'wms_wrk_no',
+        label: 'WMS宸ヤ綔鍙�',
+        tableProp: 'wmsWrkNo',
+        exportField: 'wmsWrkNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'systemMsg',
+        columnName: 'system_msg',
+        label: '',
+        tableProp: 'systemMsg',
+        exportField: 'systemMsg',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'mk',
+        columnName: 'mk',
+        label: '',
+        tableProp: 'mk',
+        exportField: 'mk',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'wrkSts',
+        columnName: 'wrk_sts',
+        label: '宸ヤ綔鐘舵��',
+        tableProp: 'wrkSts',
+        exportField: 'wrkSts',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioType',
+        columnName: 'io_type',
+        label: '鍏ュ嚭搴撶被鍨�',
+        tableProp: 'ioType',
+        exportField: 'ioType',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioPri',
+        columnName: 'io_pri',
+        label: '浼樺厛绾�',
+        tableProp: 'ioPri',
+        exportField: 'ioPri',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: 110,
+        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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'ioTime',
+        columnName: 'io_time',
+        label: '宸ヤ綔鏃堕棿',
+        tableProp: 'ioTime$',
+        exportField: 'ioTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '宸ヤ綔鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorTime',
+        columnName: 'error_time',
+        label: '',
+        tableProp: 'errorTime$',
+        exportField: 'errorTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorMemo',
+        columnName: 'error_memo',
+        label: '',
+        tableProp: 'errorMemo',
+        exportField: 'errorMemo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: '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: 'shuttleNo',
+        columnName: 'shuttle_no',
+        label: '',
+        tableProp: 'shuttleNo',
+        exportField: 'shuttleNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'liftNo',
+        columnName: 'lift_no',
+        label: '',
+        tableProp: 'liftNo',
+        exportField: 'liftNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wmsWrkNo',
+        columnName: 'wms_wrk_no',
+        label: 'WMS宸ヤ綔鍙�',
+        tableProp: 'wmsWrkNo',
+        exportField: 'wmsWrkNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'systemMsg',
+        columnName: 'system_msg',
+        label: '',
+        tableProp: 'systemMsg',
+        exportField: 'systemMsg',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'mk',
+        columnName: 'mk',
+        label: '',
+        tableProp: 'mk',
+        exportField: 'mk',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'wrkSts',
+        columnName: 'wrk_sts',
+        label: '宸ヤ綔鐘舵��',
+        tableProp: 'wrkSts',
+        exportField: 'wrkSts',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioType',
+        columnName: 'io_type',
+        label: '鍏ュ嚭搴撶被鍨�',
+        tableProp: 'ioType',
+        exportField: 'ioType',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'ioPri',
+        columnName: 'io_pri',
+        label: '浼樺厛绾�',
+        tableProp: 'ioPri',
+        exportField: 'ioPri',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        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: 110,
+        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: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'ioTime',
+        columnName: 'io_time',
+        label: '宸ヤ綔鏃堕棿',
+        tableProp: 'ioTime$',
+        exportField: 'ioTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiUser',
+        columnName: 'modi_user',
+        label: '淇敼浜哄憳',
+        tableProp: 'modiUser',
+        exportField: 'modiUser',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'modiTime',
+        columnName: 'modi_time',
+        label: '淇敼鏃堕棿',
+        tableProp: 'modiTime$',
+        exportField: 'modiTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeUser',
+        columnName: 'appe_user',
+        label: '鍒涘缓鑰�',
+        tableProp: 'appeUser',
+        exportField: 'appeUser',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'appeTime',
+        columnName: 'appe_time',
+        label: '宸ヤ綔鏃堕棿',
+        tableProp: 'appeTime$',
+        exportField: 'appeTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorTime',
+        columnName: 'error_time',
+        label: '',
+        tableProp: 'errorTime$',
+        exportField: 'errorTime$',
+        kind: 'date',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 168,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'errorMemo',
+        columnName: 'error_memo',
+        label: '',
+        tableProp: 'errorMemo',
+        exportField: 'errorMemo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: true,
+        minWidth: 180,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        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: '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: 'crnNo',
+        columnName: 'crn_no',
+        label: '鍫嗗灈鏈�',
+        tableProp: 'crnNo',
+        exportField: 'crnNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'dualCrnNo',
+        columnName: 'dual_crn_no',
+        label: '鍙屽伐浣嶅爢鍨涙満',
+        tableProp: 'dualCrnNo',
+        exportField: 'dualCrnNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'rgvNo',
+        columnName: 'rgv_no',
+        label: '',
+        tableProp: 'rgvNo',
+        exportField: 'rgvNo',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    },
+    {
+        field: 'wmsWrkNo',
+        columnName: 'wms_wrk_no',
+        label: 'WMS宸ヤ綔鍙�',
+        tableProp: 'wmsWrkNo',
+        exportField: 'wmsWrkNo',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'systemMsg',
+        columnName: 'system_msg',
+        label: '',
+        tableProp: 'systemMsg',
+        exportField: 'systemMsg',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'batch',
+        columnName: 'batch',
+        label: '鎵规',
+        tableProp: 'batch',
+        exportField: 'batch',
+        kind: 'text',
+        valueType: 'string',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: 'Y',
+        checkboxInactiveRaw: 'N'
+    },
+    {
+        field: 'batchSeq',
+        columnName: 'batch_seq',
+        label: '鎵规搴忓垪',
+        tableProp: 'batchSeq',
+        exportField: 'batchSeq',
+        kind: 'text',
+        valueType: 'number',
+        required: false,
+        primaryKey: false,
+        sortable: false,
+        textarea: false,
+        minWidth: 110,
+        enumOptions: [],
+        foreignQuery: '',
+        checkboxActiveRaw: '1',
+        checkboxInactiveRaw: '0'
+    }
 
-    // 鏁版嵁娓叉煋
-    tableIns = table.render({
-        elem: '#wrkMastLog',
-        headers: {token: localStorage.getItem('token')},
-        url: baseUrl+'/wrkMastLog/list/auth',
-        page: true,
-        limit: 16,
-        limits: [16, 30, 50, 100, 200, 500],
-        even: true,
-        toolbar: '#toolbar',
-        cellMinWidth: 50,
-        cols: [[
-            {field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�',event: 'wrkNo', sort: true}
-            ,{field: 'wmsWrkNo', align: 'center',title: 'WMS宸ヤ綔鍙�'}
-            ,{field: 'appeTime$', align: 'center',title: '宸ヤ綔鏃堕棿', width:160, sort: true}
-            ,{field: 'wrkSts$', align: 'center',title: '宸ヤ綔鐘舵��', width:160}
-            ,{field: 'ioType$', align: 'center',title: '鍏ュ嚭搴撶被鍨�', width:160}
-            ,{field: 'ioPri', align: 'center',title: '浼樺厛绾�'}
-            ,{field: 'sourceStaNo', align: 'center',title: '婧愮珯'}
-            ,{field: 'staNo', align: 'center',title: '鐩爣绔�'}
-            ,{field: 'sourceLocNo', align: 'center',title: '婧愬簱浣�'}
-            ,{field: 'locNo', align: 'center',title: '鐩爣搴撲綅'}
-            ,{field: 'crnNo', align: 'center',title: '鍫嗗灈鏈�'}
-            ,{field: 'dualCrnNo', align: 'center',title: '鍙屽伐浣嶅爢鍨涙満'}
-            ,{field: 'batch', align: 'center',title: '鎵规'}
-            ,{field: 'batchSeq', align: 'center',title: '鎵规搴忓垪'}
-            ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳', hide:true}
-            ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿', hide:true}
-            // ,{field: 'appeUser$', align: 'center',title: '鍒涘缓鑰�',event: 'appeUser', style: 'cursor:pointer'}
-            // ,{field: 'appeTime$', align: 'center',title: '娣诲姞鏃堕棿'}
-            ,{field: 'barcode', align: 'center',title: '鏉$爜'}
-            // ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width: 80}
-        ]],
-        request: {
-            pageName: 'curr',
-            pageSize: 'limit'
-        },
-        parseData: function (res) {
-            return {
-                'code': res.code,
-                'msg': res.msg,
-                'count': res.data.total,
-                'data': res.data.records
+    ]);
+
+    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;
             }
-        },
-        response: {
-            statusCode: 200
-        },
-        done: function(res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+            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;
             }
-            pageCurr=curr;
-            limit();
-            form.on('checkbox(tableCheckbox)', function (data) {
-                var _index = $(data.elem).attr('table-index')||0;
-                if(data.elem.checked){
-                    res.data[_index][data.value] = 'Y';
-                }else{
-                    res.data[_index][data.value] = 'N';
+            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([]);
                 }
             });
-        }
-    });
-
-    // 鐩戝惉鎺掑簭浜嬩欢
-    table.on('sort(wrkMastLog)', function (obj) {
-        var searchData = {};
-        $.each($('#search-box [name]').serializeArray(), function() {
-            searchData[this.name] = this.value;
-        });
-        searchData['orderByField'] = obj.field;
-        searchData['orderByType'] = obj.type;
-        tableIns.reload({
-            where: searchData,
-            page: {
-                curr: 1
-            },
-            done: function (res, curr, count) {
-                if (res.code === 403) {
-                    top.location.href = baseUrl+"/";
-                }
-                pageCurr=curr;
-                limit();
-            }
-        });
-    });
-
-    // 鐩戝惉澶村伐鍏锋爮浜嬩欢
-    table.on('toolbar(wrkMastLog)', function (obj) {
-        var checkStatus = table.checkStatus(obj.config.id);
-        switch(obj.event) {
-            case 'addData':
-                layer.open({
-                    type: 2,
-                    title: '鏂板',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'wrkMastLog_detail.html',
-                    success: function(layero, index){
-                        layer.getChildFrame('#data-detail-submit-edit', index).hide();
-                    	clearFormVal(layer.getChildFrame('#detail', index));
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-                    }
-                });
-                break;
-            case 'refreshData':
-                tableIns.reload({
-                    page: {
-                        curr: pageCurr
-                    }
-                });
-                limit();
-                break;
-            case 'deleteData':
-                var data = checkStatus.data;
-                if (data.length === 0){
-                    layer.msg('璇烽�夋嫨鏁版嵁');
-                } else {
-                    layer.confirm('纭畾鍒犻櫎'+(data.length===1?'姝�':data.length)+'鏉℃暟鎹悧', function(){
-                        $.ajax({
-                            url: baseUrl+"/wrkMastLog/delete/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: {param: JSON.stringify(data)},
-                            method: 'POST',
-                            traditional:true,
-                            success: function (res) {
-                                if (res.code === 200){
-                                    layer.closeAll();
-                                    tableReload(false);
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                } else {
-                                    layer.msg(res.msg)
-                                }
-                            }
-                        })
-                    });
-                }
-                break;
-            case 'exportData':
-                layer.confirm('纭畾瀵煎嚭Excel鍚�', {shadeClose: true}, function(){
-                    var titles=[];
-                    var fields=[];
-                    obj.config.cols[0].map(function (col) {
-                        if (col.type === 'normal' && col.hide === false && col.toolbar == null) {
-                            titles.push(col.title);
-                            fields.push(col.field);
-                        }
-                    });
-                    var exportData = {};
-                    $.each($('#search-box [name]').serializeArray(), function() {
-                        exportData[this.name] = this.value;
-                    });
-                    var param = {
-                        'wrkMastLog': exportData,
-                        'fields': fields
-                    };
-                    $.ajax({
-                        url: baseUrl+"/wrkMastLog/export/auth",
-                        headers: {'token': localStorage.getItem('token')},
-                        data: JSON.stringify(param),
-                        dataType:'json',
-                        contentType:'application/json;charset=UTF-8',
-                        method: 'POST',
-                        success: function (res) {
-                            layer.closeAll();
-                            if (res.code === 200) {
-                                table.exportFile(titles,res.data,'xls');
-                            } else if (res.code === 403) {
-                                top.location.href = baseUrl+"/";
-                            } else {
-                                layer.msg(res.msg)
-                            }
-                        }
-                    });
-                });
-                break;
-        }
-    });
-
-    // 鐩戝惉琛屽伐鍏蜂簨浠�
-    table.on('tool(wrkMastLog)', function(obj){
-        var data = obj.data;
-        switch (obj.event) {
-            // 鏄庣粏灞曠ず
-            case 'detlShow':
-                wrkNo = data.wrkNo;
-                ioTime =  data.ioTime;
-                // 琛ㄦ牸涓嬫柟鏄剧ず
-                // locDetl(data.wrkNo);
-                // 寮瑰眰鏄剧ず
-                layer.open({
-                    type: 2,
-                    title: '宸ヤ綔鏄庣粏鍘嗗彶妗�',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: true,
-                    content: 'wrkDetlLog.html',
-                    success: function(layero, index){
-                    }
-                });
-                break;
-            // 璇︽儏
-            case 'detail':
-                layer.open({
-                    type: 2,
-                    title: '璇︽儏',
-                    maxmin: true,
-                    area: [top.detailWidth, top.detailHeight],
-                    shadeClose: false,
-                    content: 'wrkMastLog_detail.html',
-                    success: function(layero, index){
-                        setFormVal(layer.getChildFrame('#detail', index), data, true);
-                        top.convertDisabled(layer.getChildFrame('#data-detail :input', index), true);
-                        layero.find('iframe')[0].contentWindow.layui.form.render('select');
-                        layero.find('iframe')[0].contentWindow.layui.form.render('checkbox');
-                        layer.getChildFrame('#data-detail-submit-save,#data-detail-submit-edit,#prompt', index).hide();
-                        layer.getChildFrame('##dealDownLine', index).hide();
-                        layer.iframeAuto(index);layer.style(index, {top: (($(window).height()-layer.getChildFrame('#data-detail', index).height())/3)+"px"});
-
-                    }
-                });
-                break;
-        }
-    });
-
-    // 鏁版嵁淇濆瓨鍔ㄤ綔
-    form.on('submit(save)', function () {
-        if (banMsg != null){
-            layer.msg(banMsg);
-            return;
-        }
-        method("add");
-    });
-
-    // 鏁版嵁淇敼鍔ㄤ綔
-    form.on('submit(edit)', function () {
-        method("update")
-    });
-
-    function method(name){
-        var index = layer.load(1, {
-            shade: [0.5,'#000'] //0.1閫忔槑搴︾殑鑳屾櫙
-        });
-        var data = {
-//            id: $('#id').val(),
-            id: $('#id').val(),
-            wrkNo: $('#wrkNo').val(),
-            invWh: $('#invWh').val(),
-            ymd: top.strToDate($('#ymd\\$').val()),
-            mk: $('#mk').val(),
-            whsType: $('#whsType').val(),
-            wrkSts: $('#wrkSts').val(),
-            ioType: $('#ioType').val(),
-            crnNo: $('#crnNo').val(),
-            sheetNo: $('#sheetNo').val(),
-            ioPri: $('#ioPri').val(),
-            wrkDate: top.strToDate($('#wrkDate\\$').val()),
-            locNo: $('#locNo').val(),
-            staNo: $('#staNo').val(),
-            sourceStaNo: $('#sourceStaNo').val(),
-            sourceLocNo: $('#sourceLocNo').val(),
-            locSts: $('#locSts').val(),
-            picking: $('#picking').val(),
-            linkMis: $('#linkMis').val(),
-            onlineYn: $('#onlineYn').val(),
-            updMk: $('#updMk').val(),
-            exitMk: $('#exitMk').val(),
-            pltType: $('#pltType').val(),
-            emptyMk: $('#emptyMk').val(),
-            ioTime: top.strToDate($('#ioTime\\$').val()),
-            ctnType: $('#ctnType').val(),
-            packed: $('#packed').val(),
-            oveMk: $('#oveMk').val(),
-            mtnType: $('#mtnType').val(),
-            userNo: $('#userNo').val(),
-            crnStrTime: top.strToDate($('#crnStrTime\\$').val()),
-            crnEndTime: top.strToDate($('#crnEndTime\\$').val()),
-            plcStrTime: top.strToDate($('#plcStrTime\\$').val()),
-            crnPosTime: top.strToDate($('#crnPosTime\\$').val()),
-            loadTime: $('#loadTime').val(),
-            expTime: $('#expTime').val(),
-            refWrkno: $('#refWrkno').val(),
-            refIotime: top.strToDate($('#refIotime\\$').val()),
-            modiUser: $('#modiUser').val(),
-            modiTime: top.strToDate($('#modiTime\\$').val()),
-            appeUser: $('#appeUser').val(),
-            appeTime: top.strToDate($('#appeTime\\$').val()),
-            pauseMk: $('#pauseMk').val(),
-            errorTime: top.strToDate($('#errorTime\\$').val()),
-            errorMemo: $('#errorMemo').val(),
-            ctnKind: $('#ctnKind').val(),
-            manuType: $('#manuType').val(),
-            memoM: $('#memoM').val(),
-            scWeight: $('#scWeight').val(),
-            logMk: $('#logMk').val(),
-            logErrTime: top.strToDate($('#logErrTime\\$').val()),
-            logErrMemo: $('#logErrMemo').val(),
-            barcode: $('#barcode').val(),
-            PdcType: $('#PdcType').val(),
-            ctnNo: $('#ctnNo').val(),
-            fullPlt: $('#fullPlt').val(),
-
-        };
-        $.ajax({
-            url: baseUrl+"/wrkMastLog/"+name+"/auth",
-            headers: {'token': localStorage.getItem('token')},
-            data: top.reObject(data),
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    parent.layer.closeAll();
-                    parent.$(".layui-laypage-btn")[0].click();
-                    $("#data-detail :input").each(function () {
-                        $(this).val("");
-                    });
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                }else {
-                    layer.msg(res.msg)
-                }
-                layer.close(index);
-            }
-        })
-    }
-
-    // 澶嶉�夋浜嬩欢
-    form.on('checkbox(detailCheckbox)', function (data) {
-        var el = data.elem;
-        if (el.checked) {
-            $(el).val('Y');
-        } else {
-            $(el).val('N');
-        }
-    });
-
-    // 鎼滅储鏍忔悳绱簨浠�
-    form.on('submit(search)', function (data) {
-        pageCurr = 1;
-        tableReload(false);
-    });
-
-    // 鎼滅储鏍忛噸缃簨浠�
-    form.on('submit(reset)', function (data) {
-        pageCurr = 1;
-        clearFormVal($('#search-box'));
-        tableReload(false);
-    });
-
-    // 鏃堕棿閫夋嫨鍣�
-    layDate.render({
-        elem: '#ymd\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#wrkDate\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#ioTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#crnStrTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#crnEndTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#plcStrTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#crnPosTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#refIotime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#modiTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#appeTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#errorTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '#logErrTime\\$',
-        type: 'datetime'
-    });
-    layDate.render({
-        elem: '.layui-laydate-range'
-        ,type: 'datetime'
-        ,range: true
-    });
-
-});
-
-// 鍏抽棴鍔ㄤ綔
-$(document).on('click','#data-detail-close', function () {
-    parent.layer.closeAll();
-});
-
-function tableReload(child) {
-    var searchData = {};
-    $.each($('#search-box [name]').serializeArray(), function() {
-        searchData[this.name] = this.value;
-    });
-    (child ? parent.tableIns : tableIns).reload({
-        where: searchData,
-        page: {
-            curr: pageCurr
         },
-        done: function (res, curr, count) {
-            if (res.code === 403) {
-                top.location.href = baseUrl+"/";
+        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;
             }
-            pageCurr=curr;
-            if (res.data.length === 0 && count !== 0) {
-                tableIns.reload({
-                    where: searchData,
+            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: pageCurr-1
+                        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);
                     }
-                });
-                pageCurr -= 1;
-            }
-            limit(child);
-        }
-    });
-}
-
-function setFormVal(el, data, showImg) {
-    for (var val in data) {
-        var find = el.find(":input[id='" + val + "']");
-        if (find[0]!=null){
-            if (find[0].type === 'checkbox'){
-                if (data[val]==='Y'){
-                    find.attr("checked","checked");
-                    find.val('Y');
-                } else {
-                    find.remove("checked");
-                    find.val('N');
+                    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('瀵煎嚭澶辫触');
+                        }
+                    });
                 }
-                continue;
-            }
-        }
-        find.val(data[val]);
-        if (showImg){
-            var next = find.next();
-            if (next.get(0)){
-                if (next.get(0).localName === "img") {
-                    find.hide();
-                    next.attr("src", data[val]);
-                    next.show();
-                }
-            }
-        }
+            })
+        });
     }
-}
 
-function clearFormVal(el) {
-    $(':input', el)
-        .val('')
-        .removeAttr('checked')
-        .removeAttr('selected');
-}
-
-function detailScreen(index) {
-    var detail = layer.getChildFrame('#data-detail', index);
-    var height = detail.height()+60;
-    if (height > ($(window).height()*0.9)) {
-        height = ($(window).height()*0.8);
-    }
-    layer.style(index, {
-//        top: (($(window).height()-height)/3)+"px",
-        height: height+'px'
-    });
-}
-
-$('body').keydown(function () {
-    if (event.keyCode === 13) {
-        $("#search").click();
-    }
-});
+})();
diff --git a/src/main/webapp/views/apiLog/apiLog.html b/src/main/webapp/views/apiLog/apiLog.html
index 92f9fbc..b1a68b8 100644
--- a/src/main/webapp/views/apiLog/apiLog.html
+++ b/src/main/webapp/views/apiLog/apiLog.html
@@ -1,187 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>ApiLog 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="namespace" placeholder="鍚嶇О绌洪棿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="apiLog" lay-filter="apiLog"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 ApiLog' : '淇敼 ApiLog'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-<!--        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>-->
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-<!--    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>-->
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
-<script type="text/html" id="resTpl">
-    <span name="settle"
-          {{# if( d.result === 1){ }}
-          class="layui-badge layui-badge-green" >{{d.result$}}</span>
-    {{# }else { }}
-    class="layui-badge layui-badge-red" >{{d.result$}}</span>
-    {{# } }}
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/apiLog/apiLog.js" charset="utf-8"></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/apiLog/apiLog.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏃ュ織缂栧彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="uuid" placeholder="璇疯緭鍏ユ棩蹇楃紪鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍚嶇О绌洪棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="namespace" placeholder="璇疯緭鍏ュ悕绉扮┖闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鎺ュ彛鍦板潃: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="url" placeholder="璇疯緭鍏ユ帴鍙e湴鍧�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">骞冲彴瀵嗛挜: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="appkey" placeholder="璇疯緭鍏ュ钩鍙板瘑閽�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏃堕棿鎴�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="timestamp" placeholder="璇疯緭鍏ユ椂闂存埑">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">瀹㈡埛绔疘P: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="clientIp" placeholder="璇疯緭鍏ュ鎴风IP">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璇锋眰鍐呭: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="request" placeholder="璇疯緭鍏ヨ姹傚唴瀹�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍝嶅簲鍐呭: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="response" placeholder="璇疯緭鍏ュ搷搴斿唴瀹�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鍐呭: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="err" placeholder="璇疯緭鍏ュ紓甯稿唴瀹�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">缁撴灉: </label>
-                    <div class="layui-input-block">
-                        <select name="result">
-                            <option value="">璇烽�夋嫨缁撴灉</option>
-                            <option value="1">鎴愬姛</option>
-                            <option value="0">澶辫触</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="status">
-                            <option value="">璇烽�夋嫨鐘舵��</option>
-                            <option value="1">姝e父</option>
-                            <option value="0">绂佺敤</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="createTime" id="createTime$" placeholder="璇疯緭鍏ユ坊鍔犳椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basCrnp/basCrnp.html b/src/main/webapp/views/basCrnp/basCrnp.html
index 76ba096..d1031f5 100644
--- a/src/main/webapp/views/basCrnp/basCrnp.html
+++ b/src/main/webapp/views/basCrnp/basCrnp.html
@@ -1,156 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasCrnp 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basCrnp" lay-filter="basCrnp"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasCrnp' : '淇敼 BasCrnp'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basCrnp/basCrnp.js" charset="utf-8"></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/basCrnp/basCrnp.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="crnNo" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="status">
-                            <option value="">璇烽�夋嫨鐘舵��</option>
-                            <option value="1">姝e父</option>
-                            <option value="0">绂佺敤</option>
-                        </select>
-                    </div>
-                </div>
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">宸ヤ綔鍙�: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="wrkNo" placeholder="璇疯緭鍏ュ伐浣滃彿">-->
-<!--                    </div>-->
-<!--                </div>-->
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍙叆(checkBox): </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="inEnable" placeholder="璇疯緭鍏ュ彲鍏�(checkBox)">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍙嚭(checkBox): </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="outEnable" placeholder="璇疯緭鍏ュ彲鍑�(checkBox)">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鎺у埗搴撲綅鎺掑彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="controlRows" placeholder="璇疯緭鍏ユ帶鍒跺簱浣嶆帓鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣卞簱浣嶆帓鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="deepRows" placeholder="璇疯緭鍏ユ繁搴撲綅鎺掑彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍏ュ簱绔欏垪琛�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="inStationList" placeholder="璇疯緭鍏ュ叆搴撶珯鍒楄〃">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍑哄簱绔欏垪琛�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="outStationList" placeholder="璇疯緭鍏ュ嚭搴撶珯鍒楄〃">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏈�澶у叆搴撲换鍔℃暟: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="maxInTask" placeholder="璇疯緭鍏ユ渶澶у叆搴撲换鍔℃暟">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏈�澶у嚭搴撲换鍔℃暟: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="maxOutTask" placeholder="璇疯緭鍏ユ渶澶у嚭搴撲换鍔℃暟">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basCrnp/basCrnp_detail.html b/src/main/webapp/views/basCrnp/basCrnp_detail.html
deleted file mode 100644
index 24f8690..0000000
--- a/src/main/webapp/views/basCrnp/basCrnp_detail.html
+++ /dev/null
@@ -1,112 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="crnNo" class="layui-input" type="text" onkeyup="check(this.id, 'basCrnp')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐘躲��銆�鎬侊細</label>
-            <div class="layui-input-inline">
-                <select id="status">
-                    <option value="" style="display: none"></option>
-                    <option value="1">姝e父</option>
-                    <option value="0">绂佺敤</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="wrkNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鍙叆(checkBox)锛�</label>
-            <div class="layui-input-inline">
-                <input id="inEnable" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鍙嚭(checkBox)锛�</label>
-            <div class="layui-input-inline">
-                <input id="outEnable" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="createBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basCrnp/basCrnp.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basCrnpErr/basCrnpErr.html b/src/main/webapp/views/basCrnpErr/basCrnpErr.html
index aa1c288..d920640 100644
--- a/src/main/webapp/views/basCrnpErr/basCrnpErr.html
+++ b/src/main/webapp/views/basCrnpErr/basCrnpErr.html
@@ -1,98 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasCrnpErr 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basCrnpErr" lay-filter="basCrnpErr"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasCrnpErr' : '淇敼 BasCrnpErr'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basCrnpErr/basCrnpErr.js" charset="utf-8"></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/basCrnpErr/basCrnpErr.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鐮�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="errorCode" placeholder="璇疯緭鍏ュ紓甯哥爜">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="errName" placeholder="璇疯緭鍏ュ紓甯�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basCrnpErr/basCrnpErr_detail.html b/src/main/webapp/views/basCrnpErr/basCrnpErr_detail.html
deleted file mode 100644
index afda006..0000000
--- a/src/main/webapp/views/basCrnpErr/basCrnpErr_detail.html
+++ /dev/null
@@ -1,102 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basCrnpErr')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮� 甯� 鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="errorCode" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮傘��銆�甯革細</label>
-            <div class="layui-input-inline">
-                <input id="errName" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="modiUser" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="modiUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryBymodiUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryBymodiUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="modiTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="appeUser" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="appeUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByappeUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByappeUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="appeTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basCrnpErr/basCrnpErr.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basCrnpErrLog/basCrnpErrLog.html b/src/main/webapp/views/basCrnpErrLog/basCrnpErrLog.html
index d93bef8..e8de806 100644
--- a/src/main/webapp/views/basCrnpErrLog/basCrnpErrLog.html
+++ b/src/main/webapp/views/basCrnpErrLog/basCrnpErrLog.html
@@ -1,234 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasCrnpErrLog 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basCrnpErrLog" lay-filter="basCrnpErrLog"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasCrnpErrLog' : '淇敼 BasCrnpErrLog'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-<!--        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>-->
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-<!--    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>-->
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basCrnpErrLog/basCrnpErrLog.js" charset="utf-8"></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/basCrnpErrLog/basCrnpErrLog.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="wrkNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍙戠敓鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="startTime" id="startTime$" placeholder="璇疯緭鍏ュ彂鐢熸椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">缁撴潫鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="endTime" id="endTime$" placeholder="璇疯緭鍏ョ粨鏉熸椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鐘舵��: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="wrkSts" placeholder="璇疯緭鍏ュ伐浣滅姸鎬�" style="display: none">
-                        <input id="wrkSts$" name="wrkSts$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ュ伐浣滅姸鎬�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="basWrkStatusQueryBywrkSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="basWrkStatusQueryBywrkStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍏ュ嚭搴撶被鍨�: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="ioType" placeholder="璇疯緭鍏ュ叆鍑哄簱绫诲瀷" style="display: none">
-                        <input id="ioType$" name="ioType$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ュ叆鍑哄簱绫诲瀷" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="basWrkIotypeQueryByioType" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="basWrkIotypeQueryByioTypeSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍫嗗灈鏈哄彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="crnNo" placeholder="璇疯緭鍏ュ爢鍨涙満鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣搴撲綅: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="locNo" placeholder="璇疯緭鍏ョ洰鏍囧簱浣�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣绔�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="staNo" placeholder="璇疯緭鍏ョ洰鏍囩珯">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">婧愮珯: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceStaNo" placeholder="璇疯緭鍏ユ簮绔�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">婧愬簱浣�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceLocNo" placeholder="璇疯緭鍏ユ簮搴撲綅">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏉$爜: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="barcode" placeholder="璇疯緭鍏ユ潯鐮�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鐮�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="errCode" placeholder="璇疯緭鍏ュ紓甯哥爜">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="error" placeholder="璇疯緭鍏ュ紓甯�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鎯呭喌: </label>
-                    <div class="layui-input-block">
-                        <select name="status">
-                            <option value="">璇烽�夋嫨寮傚父鎯呭喌</option>
-                            <option value="1">鏈鐞�</option>
-                            <option value="2">宸蹭慨澶�</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="createTime" id="createTime$" placeholder="璇疯緭鍏ユ坊鍔犳椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="createBy" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" style="display: none">
-                        <input id="createBy$" name="createBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryBycreateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryBycreateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" style="display: none">
-                        <input id="updateBy$" name="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绯荤粺鐘舵�佹暟鎹�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="systemStatus" placeholder="璇疯緭鍏ョ郴缁熺姸鎬佹暟鎹�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basCrnpErrLog/basCrnpErrLog_detail.html b/src/main/webapp/views/basCrnpErrLog/basCrnpErrLog_detail.html
deleted file mode 100644
index 51504be..0000000
--- a/src/main/webapp/views/basCrnpErrLog/basCrnpErrLog_detail.html
+++ /dev/null
@@ -1,202 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basCrnpErrLog')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="wrkNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍙戠敓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="startTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">缁撴潫鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="endTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸ヤ綔鐘舵�侊細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="wrkSts" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="wrkSts$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basWrkStatusQueryBywrkSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basWrkStatusQueryBywrkStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍏ュ嚭搴撶被鍨嬶細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="ioType" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="ioType$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basWrkIotypeQueryByioType" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basWrkIotypeQueryByioTypeSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍫嗗灈鏈哄彿锛�</label>
-            <div class="layui-input-inline">
-                <input id="crnNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩爣搴撲綅锛�</label>
-            <div class="layui-input-inline">
-                <input id="locNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩� 鏍� 绔欙細</label>
-            <div class="layui-input-inline">
-                <input id="staNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婧愩��銆�绔欙細</label>
-            <div class="layui-input-inline">
-                <input id="sourceStaNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婧� 搴� 浣嶏細</label>
-            <div class="layui-input-inline">
-                <input id="sourceLocNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鏉°��銆�鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="barcode" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮� 甯� 鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="errCode" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮傘��銆�甯革細</label>
-            <div class="layui-input-inline">
-                <input id="error" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮傚父鎯呭喌锛�</label>
-            <div class="layui-input-inline">
-                <select id="status">
-                    <option value="" style="display: none"></option>
-                    <option value="1">鏈鐞�</option>
-                    <option value="2">宸蹭慨澶�</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="createBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="createBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryBycreateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryBycreateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">绯荤粺鐘舵�佹暟鎹細</label>
-            <div class="layui-input-inline">
-                <input id="systemStatus" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basCrnpErrLog/basCrnpErrLog.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basCrnpOpt/basCrnpOpt.html b/src/main/webapp/views/basCrnpOpt/basCrnpOpt.html
index d868da4..34ee52d 100644
--- a/src/main/webapp/views/basCrnpOpt/basCrnpOpt.html
+++ b/src/main/webapp/views/basCrnpOpt/basCrnpOpt.html
@@ -1,174 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasCrnpOpt 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basCrnpOpt" lay-filter="basCrnpOpt"></table>
-        </div>
-    </div>
-</div>
 
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-<!--        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>-->
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-<!--    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>-->
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basCrnpOpt/basCrnpOpt.js" charset="utf-8"></script>
-</body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="wrkNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍫嗗灈鏈哄彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="crnNo" placeholder="璇疯緭鍏ュ爢鍨涙満鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">涓嬪彂鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sendTime" id="sendTime$" placeholder="璇疯緭鍏ヤ笅鍙戞椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">浣滀笟: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="mode" placeholder="璇疯緭鍏ヤ綔涓�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璧风偣搴撲綅: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceLocNo" placeholder="璇疯緭鍏ヨ捣鐐瑰簱浣�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣搴撲綅: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="targetLocNo" placeholder="璇疯緭鍏ョ洰鏍囧簱浣�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" style="display: none">
-                        <input id="updateBy$" name="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
+            <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>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍛戒护: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="command" placeholder="璇疯緭鍏ュ懡浠�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绯荤粺鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="systemStatus" placeholder="璇疯緭鍏ョ郴缁熺姸鎬�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">涓嬪彂鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="send">
-                            <option value="">璇烽�夋嫨涓嬪彂鐘舵��</option>
-                            <option value="0">鏈笅鍙�</option>
-                            <option value="1">宸蹭笅鍙�</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璇锋眰鍝嶅簲: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="response" placeholder="璇疯緭鍏ヨ姹傚搷搴�">
-                    </div>
-                </div>
+            </el-collapse-transition>
 
-             </div>
+            <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>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasCrnpOpt' : '淇敼 BasCrnpOpt'"
+        :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>
-    </form>
-</script>
+    </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/basCrnpOpt/basCrnpOpt.js?v=20260310" charset="utf-8"></script>
+</body>
 </html>
-
diff --git a/src/main/webapp/views/basCrnpOpt/basCrnpOpt_detail.html b/src/main/webapp/views/basCrnpOpt/basCrnpOpt_detail.html
deleted file mode 100644
index 1e6b83a..0000000
--- a/src/main/webapp/views/basCrnpOpt/basCrnpOpt_detail.html
+++ /dev/null
@@ -1,142 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basCrnpOpt')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="wrkNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍫嗗灈鏈哄彿锛�</label>
-            <div class="layui-input-inline">
-                <input id="crnNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">涓嬪彂鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="sendTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">浣溿��銆�涓氾細</label>
-            <div class="layui-input-inline">
-                <input id="mode" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璧风偣搴撲綅锛�</label>
-            <div class="layui-input-inline">
-                <input id="sourceLocNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩爣搴撲綅锛�</label>
-            <div class="layui-input-inline">
-                <input id="targetLocNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍛姐��銆�浠わ細</label>
-            <div class="layui-input-inline">
-                <input id="command" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">绯荤粺鐘舵�侊細</label>
-            <div class="layui-input-inline">
-                <input id="systemStatus" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">涓嬪彂鐘舵�侊細</label>
-            <div class="layui-input-inline">
-                <select id="send">
-                    <option value="" style="display: none"></option>
-                    <option value="0">鏈笅鍙�</option>
-                    <option value="1">宸蹭笅鍙�</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璇锋眰鍝嶅簲锛�</label>
-            <div class="layui-input-inline">
-                <input id="response" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basCrnpOpt/basCrnpOpt.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basDevp/basDevp.html b/src/main/webapp/views/basDevp/basDevp.html
index 73ff8d3..720bd37 100644
--- a/src/main/webapp/views/basDevp/basDevp.html
+++ b/src/main/webapp/views/basDevp/basDevp.html
@@ -1,202 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasDevp 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basDevp" lay-filter="basDevp"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasDevp' : '淇敼 BasDevp'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="initStation">鍒濆鍖栫珯鐐规暟鎹�</a>
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDevp/basDevp.js" charset="utf-8"></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/basDevp/basDevp.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璁惧缂栧彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="devpNo" placeholder="璇疯緭鍏ヨ澶囩紪鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="status">
-                            <option value="">璇烽�夋嫨鐘舵��</option>
-                            <option value="1">姝e父</option>
-                            <option value="0">绂佺敤</option>
-                        </select>
-                    </div>
-                </div>
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">鍒涘缓浜哄憳: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="createBy" placeholder="璇疯緭鍏ュ垱寤轰汉鍛�">-->
-<!--                    </div>-->
-<!--                </div>-->
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">鍒涘缓鏃堕棿: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="createTime" id="createTime$" placeholder="璇疯緭鍏ュ垱寤烘椂闂�">-->
-<!--                    </div>-->
-<!--                </div>-->
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">淇敼浜哄憳: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�">-->
-<!--                    </div>-->
-<!--                </div>-->
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">淇敼鏃堕棿: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">-->
-<!--                    </div>-->
-<!--                </div>-->
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绔欑偣鏁版嵁: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="stationList" placeholder="璇疯緭鍏ョ珯鐐规暟鎹�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏉$爜绔欑偣鏁版嵁: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="barcodeStationList" placeholder="璇疯緭鍏ユ潯鐮佺珯鐐规暟鎹�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍏ュ簱绔欑偣鏁版嵁: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="inStationList" placeholder="璇疯緭鍏ュ叆搴撶珯鐐规暟鎹�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍑哄簱绔欑偣鏁版嵁: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="outStationList" placeholder="璇疯緭鍏ュ嚭搴撶珯鐐规暟鎹�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">杩愯鍫靛閲嶆柊鍒嗛厤搴撲綅绔欑偣鏁版嵁: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="runBlockReassignLocStationList" placeholder="璇疯緭鍏ヨ繍琛屽牭濉為噸鏂板垎閰嶅簱浣嶇珯鐐规暟鎹�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍑哄簱鎺掑簭浜や簰鐐�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="isOutOrderList" placeholder="璇疯緭鍏ュ嚭搴撴帓搴忎氦浜掔偣">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">椤跺崌绉绘牻鐐�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="isLiftTransferList" placeholder="璇疯緭鍏ョ數姊腑杞偣">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
-
-<script type="text/html" id="initStationDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="devpNo" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绔欑偣璧峰缂栧彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="stationNo" placeholder="璇疯緭鍏ョ珯鐐硅捣濮嬬紪鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍒濆鍖栭暱搴�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="initLength" placeholder="璇疯緭鍏ュ垵濮嬪寲闀垮害">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">纭畾</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
diff --git a/src/main/webapp/views/basDevp/basDevp_detail.html b/src/main/webapp/views/basDevp/basDevp_detail.html
deleted file mode 100644
index 3c66b07..0000000
--- a/src/main/webapp/views/basDevp/basDevp_detail.html
+++ /dev/null
@@ -1,112 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basDevp')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璁惧缂栧彿锛�</label>
-            <div class="layui-input-inline">
-                <input id="devpNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐘躲��銆�鎬侊細</label>
-            <div class="layui-input-inline">
-                <select id="status">
-                    <option value="" style="display: none"></option>
-                    <option value="1">姝e父</option>
-                    <option value="0">绂佺敤</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="createBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">绔欑偣鏁版嵁锛�</label>
-            <div class="layui-input-inline">
-                <input id="stationList" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鏉$爜绔欑偣鏁版嵁锛�</label>
-            <div class="layui-input-inline">
-                <input id="barcodeStationList" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDevp/basDevp.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basDualCrnp/basDualCrnp.html b/src/main/webapp/views/basDualCrnp/basDualCrnp.html
index 9eb6469..9dea74e 100644
--- a/src/main/webapp/views/basDualCrnp/basDualCrnp.html
+++ b/src/main/webapp/views/basDualCrnp/basDualCrnp.html
@@ -1,192 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasDualCrnp 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basDualCrnp" lay-filter="basDualCrnp"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasDualCrnp' : '淇敼 BasDualCrnp'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDualCrnp/basDualCrnp.js" charset="utf-8"></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/basDualCrnp/basDualCrnp.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="crnNo" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="status">
-                            <option value="">璇烽�夋嫨鐘舵��</option>
-                            <option value="1">姝e父</option>
-                            <option value="0">绂佺敤</option>
-                        </select>
-                    </div>
-                </div>
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">宸ヤ綔鍙�: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="wrkNo" placeholder="璇疯緭鍏ュ伐浣滃彿">-->
-<!--                    </div>-->
-<!--                </div>-->
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍙叆(checkBox): </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="inEnable" placeholder="璇疯緭鍏ュ彲鍏�(checkBox)">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍙嚭(checkBox): </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="outEnable" placeholder="璇疯緭鍏ュ彲鍑�(checkBox)">
-                    </div>
-                </div>
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">鍒涘缓浜哄憳: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="createBy" placeholder="璇疯緭鍏ュ垱寤轰汉鍛�">-->
-<!--                    </div>-->
-<!--                </div>-->
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">鍒涘缓鏃堕棿: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="createTime" id="createTime$" placeholder="璇疯緭鍏ュ垱寤烘椂闂�">-->
-<!--                    </div>-->
-<!--                </div>-->
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">淇敼浜哄憳: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�">-->
-<!--                    </div>-->
-<!--                </div>-->
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">淇敼鏃堕棿: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">-->
-<!--                    </div>-->
-<!--                </div>-->
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鎺у埗搴撲綅鎺掑彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="controlRows" placeholder="璇疯緭鍏ユ帶鍒跺簱浣嶆帓鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣卞簱浣嶆帓鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="deepRows" placeholder="璇疯緭鍏ユ繁搴撲綅鎺掑彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍏ュ簱绔欑偣: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="inStationList" placeholder="璇疯緭鍏ュ叆搴撶珯鐐�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍑哄簱绔欑偣: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="outStationList" placeholder="璇疯緭鍏ュ嚭搴撶珯鐐�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏈�澶у叆搴撲换鍔℃暟: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="maxInTask" placeholder="璇疯緭鍏ユ渶澶у叆搴撲换鍔℃暟">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏈�澶у嚭搴撲换鍔℃暟: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="maxOutTask" placeholder="璇疯緭鍏ユ渶澶у嚭搴撲换鍔℃暟">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綅1绂佹鎵ц鍒�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="disableStationOneBays" placeholder="璇疯緭鍏ュ伐浣�1绂佹鎵ц鍒�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綅2绂佹鎵ц鍒�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="disableStationTwoBays" placeholder="璇疯緭鍏ュ伐浣�2绂佹鎵ц鍒�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basDualCrnp/basDualCrnp_detail.html b/src/main/webapp/views/basDualCrnp/basDualCrnp_detail.html
deleted file mode 100644
index ec5804c..0000000
--- a/src/main/webapp/views/basDualCrnp/basDualCrnp_detail.html
+++ /dev/null
@@ -1,148 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="crnNo" class="layui-input" type="text" onkeyup="check(this.id, 'basDualCrnp')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐘躲��銆�鎬侊細</label>
-            <div class="layui-input-inline">
-                <select id="status">
-                    <option value="" style="display: none"></option>
-                    <option value="1">姝e父</option>
-                    <option value="0">绂佺敤</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="wrkNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鍙叆(checkBox)锛�</label>
-            <div class="layui-input-inline">
-                <input id="inEnable" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鍙嚭(checkBox)锛�</label>
-            <div class="layui-input-inline">
-                <input id="outEnable" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="createBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鎺у埗搴撲綅鎺掑彿锛�</label>
-            <div class="layui-input-inline">
-                <input id="controlRows" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣卞簱浣嶆帓鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="deepRows" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍏ュ簱绔欑偣锛�</label>
-            <div class="layui-input-inline">
-                <input id="inStationList" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍑哄簱绔欑偣锛�</label>
-            <div class="layui-input-inline">
-                <input id="outStationList" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鏈�澶у叆搴撲换鍔℃暟锛�</label>
-            <div class="layui-input-inline">
-                <input id="maxInTask" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鏈�澶у嚭搴撲换鍔℃暟锛�</label>
-            <div class="layui-input-inline">
-                <input id="maxOutTask" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDualCrnp/basDualCrnp.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basDualCrnpErr/basDualCrnpErr.html b/src/main/webapp/views/basDualCrnpErr/basDualCrnpErr.html
index be2f092..f2b695a 100644
--- a/src/main/webapp/views/basDualCrnpErr/basDualCrnpErr.html
+++ b/src/main/webapp/views/basDualCrnpErr/basDualCrnpErr.html
@@ -1,134 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasDualCrnpErr 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basDualCrnpErr" lay-filter="basDualCrnpErr"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasDualCrnpErr' : '淇敼 BasDualCrnpErr'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDualCrnpErr/basDualCrnpErr.js" charset="utf-8"></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/basDualCrnpErr/basDualCrnpErr.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鐮�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="errorCode" placeholder="璇疯緭鍏ュ紓甯哥爜">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="errName" placeholder="璇疯緭鍏ュ紓甯�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="modiUser" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" style="display: none">
-                        <input id="modiUser$" name="modiUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryBymodiUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryBymodiUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="modiTime" id="modiTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="appeUser" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" style="display: none">
-                        <input id="appeUser$" name="appeUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryByappeUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryByappeUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="appeTime" id="appeTime$" placeholder="璇疯緭鍏ユ坊鍔犳椂闂�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basDualCrnpErr/basDualCrnpErr_detail.html b/src/main/webapp/views/basDualCrnpErr/basDualCrnpErr_detail.html
deleted file mode 100644
index b8ea7ad..0000000
--- a/src/main/webapp/views/basDualCrnpErr/basDualCrnpErr_detail.html
+++ /dev/null
@@ -1,102 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basDualCrnpErr')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮� 甯� 鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="errorCode" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮傘��銆�甯革細</label>
-            <div class="layui-input-inline">
-                <input id="errName" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="modiUser" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="modiUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryBymodiUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryBymodiUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="modiTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="appeUser" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="appeUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByappeUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByappeUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="appeTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDualCrnpErr/basDualCrnpErr.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basDualCrnpErrLog/basDualCrnpErrLog.html b/src/main/webapp/views/basDualCrnpErrLog/basDualCrnpErrLog.html
index 4604061..ff13672 100644
--- a/src/main/webapp/views/basDualCrnpErrLog/basDualCrnpErrLog.html
+++ b/src/main/webapp/views/basDualCrnpErrLog/basDualCrnpErrLog.html
@@ -1,234 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasDualCrnpErrLog 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basDualCrnpErrLog" lay-filter="basDualCrnpErrLog"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasDualCrnpErrLog' : '淇敼 BasDualCrnpErrLog'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDualCrnpErrLog/basDualCrnpErrLog.js" charset="utf-8"></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/basDualCrnpErrLog/basDualCrnpErrLog.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="wrkNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍙戠敓鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="startTime" id="startTime$" placeholder="璇疯緭鍏ュ彂鐢熸椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">缁撴潫鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="endTime" id="endTime$" placeholder="璇疯緭鍏ョ粨鏉熸椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鐘舵��: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="wrkSts" placeholder="璇疯緭鍏ュ伐浣滅姸鎬�" style="display: none">
-                        <input id="wrkSts$" name="wrkSts$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ュ伐浣滅姸鎬�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="basWrkStatusQueryBywrkSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="basWrkStatusQueryBywrkStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍏ュ嚭搴撶被鍨�: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="ioType" placeholder="璇疯緭鍏ュ叆鍑哄簱绫诲瀷" style="display: none">
-                        <input id="ioType$" name="ioType$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ュ叆鍑哄簱绫诲瀷" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="basWrkIotypeQueryByioType" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="basWrkIotypeQueryByioTypeSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍫嗗灈鏈哄彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="crnNo" placeholder="璇疯緭鍏ュ爢鍨涙満鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣搴撲綅: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="locNo" placeholder="璇疯緭鍏ョ洰鏍囧簱浣�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣绔�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="staNo" placeholder="璇疯緭鍏ョ洰鏍囩珯">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">婧愮珯: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceStaNo" placeholder="璇疯緭鍏ユ簮绔�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">婧愬簱浣�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceLocNo" placeholder="璇疯緭鍏ユ簮搴撲綅">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏉$爜: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="barcode" placeholder="璇疯緭鍏ユ潯鐮�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鐮�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="errCode" placeholder="璇疯緭鍏ュ紓甯哥爜">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="error" placeholder="璇疯緭鍏ュ紓甯�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鎯呭喌: </label>
-                    <div class="layui-input-block">
-                        <select name="status">
-                            <option value="">璇烽�夋嫨寮傚父鎯呭喌</option>
-                            <option value="1">鏈鐞�</option>
-                            <option value="2">宸蹭慨澶�</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="createTime" id="createTime$" placeholder="璇疯緭鍏ユ坊鍔犳椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="createBy" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" style="display: none">
-                        <input id="createBy$" name="createBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryBycreateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryBycreateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" style="display: none">
-                        <input id="updateBy$" name="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绯荤粺鐘舵�佹暟鎹�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="systemStatus" placeholder="璇疯緭鍏ョ郴缁熺姸鎬佹暟鎹�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basDualCrnpErrLog/basDualCrnpErrLog_detail.html b/src/main/webapp/views/basDualCrnpErrLog/basDualCrnpErrLog_detail.html
deleted file mode 100644
index f18ad2b..0000000
--- a/src/main/webapp/views/basDualCrnpErrLog/basDualCrnpErrLog_detail.html
+++ /dev/null
@@ -1,202 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basDualCrnpErrLog')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="wrkNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍙戠敓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="startTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">缁撴潫鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="endTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸ヤ綔鐘舵�侊細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="wrkSts" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="wrkSts$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basWrkStatusQueryBywrkSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basWrkStatusQueryBywrkStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍏ュ嚭搴撶被鍨嬶細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="ioType" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="ioType$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basWrkIotypeQueryByioType" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basWrkIotypeQueryByioTypeSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍫嗗灈鏈哄彿锛�</label>
-            <div class="layui-input-inline">
-                <input id="crnNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩爣搴撲綅锛�</label>
-            <div class="layui-input-inline">
-                <input id="locNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩� 鏍� 绔欙細</label>
-            <div class="layui-input-inline">
-                <input id="staNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婧愩��銆�绔欙細</label>
-            <div class="layui-input-inline">
-                <input id="sourceStaNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婧� 搴� 浣嶏細</label>
-            <div class="layui-input-inline">
-                <input id="sourceLocNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鏉°��銆�鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="barcode" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮� 甯� 鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="errCode" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮傘��銆�甯革細</label>
-            <div class="layui-input-inline">
-                <input id="error" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮傚父鎯呭喌锛�</label>
-            <div class="layui-input-inline">
-                <select id="status">
-                    <option value="" style="display: none"></option>
-                    <option value="1">鏈鐞�</option>
-                    <option value="2">宸蹭慨澶�</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="createBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="createBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryBycreateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryBycreateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">绯荤粺鐘舵�佹暟鎹細</label>
-            <div class="layui-input-inline">
-                <input id="systemStatus" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDualCrnpErrLog/basDualCrnpErrLog.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basDualCrnpOpt/basDualCrnpOpt.html b/src/main/webapp/views/basDualCrnpOpt/basDualCrnpOpt.html
index 8343165..4f05a67 100644
--- a/src/main/webapp/views/basDualCrnpOpt/basDualCrnpOpt.html
+++ b/src/main/webapp/views/basDualCrnpOpt/basDualCrnpOpt.html
@@ -1,174 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasDualCrnpOpt 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basDualCrnpOpt" lay-filter="basDualCrnpOpt"></table>
-        </div>
-    </div>
-</div>
 
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDualCrnpOpt/basDualCrnpOpt.js" charset="utf-8"></script>
-</body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="wrkNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍫嗗灈鏈哄彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="crnNo" placeholder="璇疯緭鍏ュ爢鍨涙満鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">涓嬪彂鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sendTime" id="sendTime$" placeholder="璇疯緭鍏ヤ笅鍙戞椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">浣滀笟: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="mode" placeholder="璇疯緭鍏ヤ綔涓�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璧风偣搴撲綅: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceLocNo" placeholder="璇疯緭鍏ヨ捣鐐瑰簱浣�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣搴撲綅: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="targetLocNo" placeholder="璇疯緭鍏ョ洰鏍囧簱浣�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" style="display: none">
-                        <input id="updateBy$" name="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
+            <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>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍛戒护: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="command" placeholder="璇疯緭鍏ュ懡浠�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绯荤粺鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="systemStatus" placeholder="璇疯緭鍏ョ郴缁熺姸鎬�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">涓嬪彂鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="send">
-                            <option value="">璇烽�夋嫨涓嬪彂鐘舵��</option>
-                            <option value="0">鏈笅鍙�</option>
-                            <option value="1">宸蹭笅鍙�</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璇锋眰鍝嶅簲: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="response" placeholder="璇疯緭鍏ヨ姹傚搷搴�">
-                    </div>
-                </div>
+            </el-collapse-transition>
 
-             </div>
+            <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>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasDualCrnpOpt' : '淇敼 BasDualCrnpOpt'"
+        :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>
-    </form>
-</script>
+    </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/basDualCrnpOpt/basDualCrnpOpt.js?v=20260310" charset="utf-8"></script>
+</body>
 </html>
-
diff --git a/src/main/webapp/views/basDualCrnpOpt/basDualCrnpOpt_detail.html b/src/main/webapp/views/basDualCrnpOpt/basDualCrnpOpt_detail.html
deleted file mode 100644
index 4caa327..0000000
--- a/src/main/webapp/views/basDualCrnpOpt/basDualCrnpOpt_detail.html
+++ /dev/null
@@ -1,142 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basDualCrnpOpt')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="wrkNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍫嗗灈鏈哄彿锛�</label>
-            <div class="layui-input-inline">
-                <input id="crnNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">涓嬪彂鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="sendTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">浣溿��銆�涓氾細</label>
-            <div class="layui-input-inline">
-                <input id="mode" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璧风偣搴撲綅锛�</label>
-            <div class="layui-input-inline">
-                <input id="sourceLocNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩爣搴撲綅锛�</label>
-            <div class="layui-input-inline">
-                <input id="targetLocNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍛姐��銆�浠わ細</label>
-            <div class="layui-input-inline">
-                <input id="command" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">绯荤粺鐘舵�侊細</label>
-            <div class="layui-input-inline">
-                <input id="systemStatus" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">涓嬪彂鐘舵�侊細</label>
-            <div class="layui-input-inline">
-                <select id="send">
-                    <option value="" style="display: none"></option>
-                    <option value="0">鏈笅鍙�</option>
-                    <option value="1">宸蹭笅鍙�</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璇锋眰鍝嶅簲锛�</label>
-            <div class="layui-input-inline">
-                <input id="response" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basDualCrnpOpt/basDualCrnpOpt.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basLocSts/basLocSts.html b/src/main/webapp/views/basLocSts/basLocSts.html
index d48d1f4..61813f4 100644
--- a/src/main/webapp/views/basLocSts/basLocSts.html
+++ b/src/main/webapp/views/basLocSts/basLocSts.html
@@ -1,54 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasLocSts 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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>
 
-<!-- 鎼滅储鏍� -->
-<div id="search-box" class="layui-form layui-card-header">
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="loc_sts" placeholder="搴撲綅鐘舵�佷唬鍙�" autocomplete="off">
+            <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>
-    </div>
+    </section>
 
-    <!-- 寰呮坊鍔� -->
-    <div id="data-search-btn" class="layui-btn-container layui-form-item" style="display: inline-block">
-        <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>
-        <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>
-    </div>
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasLocSts' : '淇敼 BasLocSts'"
+        :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>
 
-<!-- 琛ㄦ牸 -->
-<table class="layui-hide" id="basLocSts" lay-filter="basLocSts"></table>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basLocSts/basLocSts.js" charset="utf-8"></script>
-
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/basLocSts/basLocSts.js?v=20260310" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/basLocSts/basLocSts_detail.html b/src/main/webapp/views/basLocSts/basLocSts_detail.html
deleted file mode 100644
index 6d92c89..0000000
--- a/src/main/webapp/views/basLocSts/basLocSts_detail.html
+++ /dev/null
@@ -1,88 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form" style="text-align: center;">
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label" style="font-size: x-small"><span class="not-null">*</span>搴撲綅鐘舵�佷唬鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="locSts" class="layui-input" type="text" onkeyup="check(this.id, 'basLocSts')" lay-verify="required">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label" style="font-size: x-small">搴撲綅鐘舵�佹弿杩帮細</label>
-            <div class="layui-input-inline">
-                <input id="locDesc" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="modiUser" class="layui-input" type="text" style="display: none">
-                <input id="modiUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryBymodiUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryBymodiUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="modiTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">鍒� 寤� 鑰咃細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="appeUser" class="layui-input" type="text" style="display: none">
-                <input id="appeUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByappeUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByappeUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="appeTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basLocSts/basLocSts.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basMap/basMap.html b/src/main/webapp/views/basMap/basMap.html
index 235672d..b5f6011 100644
--- a/src/main/webapp/views/basMap/basMap.html
+++ b/src/main/webapp/views/basMap/basMap.html
@@ -1,127 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasMap 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basMap" lay-filter="basMap"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasMap' : '淇敼 BasMap'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button type="button" class="layui-btn demo-class-accept" lay-options="{accept: 'file'}">
-            <i class="layui-icon layui-icon-upload"></i>
-            瀵煎叆鍦板浘
-        </button>
-        <button class="layui-btn layui-btn-sm layui-btn-primary" id="btn-initLocMast" lay-event="initLocMast">鍒濆鍖栧簱浣�</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basMap/basMap.js" charset="utf-8"></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/basMap/basMap.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">灞傛暟: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="lev" placeholder="璇疯緭鍏�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍘熷鍦板浘: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="originData" placeholder="璇疯緭鍏�">
-                    </div>
-                </div>
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">鍩哄噯鎺�: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="baseRow" placeholder="璇疯緭鍏�">-->
-<!--                    </div>-->
-<!--                </div>-->
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">鍩哄噯鎺�-code: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="baseRowCode" placeholder="璇疯緭鍏�">-->
-<!--                    </div>-->
-<!--                </div>-->
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">鍩哄噯鍒�: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="baseBay" placeholder="璇疯緭鍏�">-->
-<!--                    </div>-->
-<!--                </div>-->
-<!--                <div class="layui-form-item">-->
-<!--                    <label class="layui-form-label">鍩哄噯鍒�-code: </label>-->
-<!--                    <div class="layui-input-block">-->
-<!--                        <input class="layui-input" name="baseBayCode" placeholder="璇疯緭鍏�">-->
-<!--                    </div>-->
-<!--                </div>-->
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basMap/basMap_detail.html b/src/main/webapp/views/basMap/basMap_detail.html
deleted file mode 100644
index b411abc..0000000
--- a/src/main/webapp/views/basMap/basMap_detail.html
+++ /dev/null
@@ -1,90 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>#锛�</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basMap')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">瀹炴椂鏁版嵁锛�</label>
-            <div class="layui-input-inline">
-                <input id="data" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鏇存柊鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鏈�杩戞暟鎹細</label>
-            <div class="layui-input-inline">
-                <input id="lastData" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">灞傛暟锛�</label>
-            <div class="layui-input-inline">
-                <input id="lev" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍘熷鍦板浘锛�</label>
-            <div class="layui-input-inline">
-                <input id="originData" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>da
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basMap/basMap.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basRgv/basRgv.html b/src/main/webapp/views/basRgv/basRgv.html
index 48e9600..e48fe6f 100644
--- a/src/main/webapp/views/basRgv/basRgv.html
+++ b/src/main/webapp/views/basRgv/basRgv.html
@@ -1,138 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasRgv 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basRgv" lay-filter="basRgv"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasRgv' : '淇敼 BasRgv'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basRgv/basRgv.js" charset="utf-8"></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/basRgv/basRgv.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">RGV缂栧彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="rgvNo" placeholder="璇疯緭鍏GV缂栧彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="status">
-                            <option value="">璇烽�夋嫨鐘舵��</option>
-                            <option value="1">姝e父</option>
-                            <option value="0">绂佺敤</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="taskNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍒涘缓浜哄憳: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="createBy" placeholder="璇疯緭鍏ュ垱寤轰汉鍛�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍒涘缓鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="createTime" id="createTime$" placeholder="璇疯緭鍏ュ垱寤烘椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basRgv/basRgv_detail.html b/src/main/webapp/views/basRgv/basRgv_detail.html
deleted file mode 100644
index e55c192..0000000
--- a/src/main/webapp/views/basRgv/basRgv_detail.html
+++ /dev/null
@@ -1,100 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="rgvNo" class="layui-input" type="text" onkeyup="check(this.id, 'basRgv')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐘躲��銆�鎬侊細</label>
-            <div class="layui-input-inline">
-                <select id="status">
-                    <option value="" style="display: none"></option>
-                    <option value="1">姝e父</option>
-                    <option value="0">绂佺敤</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="taskNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="createBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basRgv/basRgv.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basRgvErr/basRgvErr.html b/src/main/webapp/views/basRgvErr/basRgvErr.html
index faac858..81fe5a1 100644
--- a/src/main/webapp/views/basRgvErr/basRgvErr.html
+++ b/src/main/webapp/views/basRgvErr/basRgvErr.html
@@ -1,134 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasRgvErr 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basRgvErr" lay-filter="basRgvErr"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasRgvErr' : '淇敼 BasRgvErr'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basRgvErr/basRgvErr.js" charset="utf-8"></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/basRgvErr/basRgvErr.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鐮�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="errorCode" placeholder="璇疯緭鍏ュ紓甯哥爜">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="errName" placeholder="璇疯緭鍏ュ紓甯�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="modiUser" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" style="display: none">
-                        <input id="modiUser$" name="modiUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryBymodiUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryBymodiUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="modiTime" id="modiTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="appeUser" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" style="display: none">
-                        <input id="appeUser$" name="appeUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryByappeUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryByappeUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="appeTime" id="appeTime$" placeholder="璇疯緭鍏ユ坊鍔犳椂闂�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basRgvErr/basRgvErr_detail.html b/src/main/webapp/views/basRgvErr/basRgvErr_detail.html
deleted file mode 100644
index 3443a99..0000000
--- a/src/main/webapp/views/basRgvErr/basRgvErr_detail.html
+++ /dev/null
@@ -1,102 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basRgvErr')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮� 甯� 鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="errorCode" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮傘��銆�甯革細</label>
-            <div class="layui-input-inline">
-                <input id="errName" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="modiUser" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="modiUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryBymodiUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryBymodiUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="modiTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="appeUser" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="appeUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByappeUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByappeUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="appeTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basRgvErr/basRgvErr.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basRgvErrLog/basRgvErrLog.html b/src/main/webapp/views/basRgvErrLog/basRgvErrLog.html
index 0f66b1d..6285e8f 100644
--- a/src/main/webapp/views/basRgvErrLog/basRgvErrLog.html
+++ b/src/main/webapp/views/basRgvErrLog/basRgvErrLog.html
@@ -1,234 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasRgvErrLog 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basRgvErrLog" lay-filter="basRgvErrLog"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasRgvErrLog' : '淇敼 BasRgvErrLog'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-<!--        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>-->
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basRgvErrLog/basRgvErrLog.js" charset="utf-8"></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/basRgvErrLog/basRgvErrLog.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="taskNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍙戠敓鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="startTime" id="startTime$" placeholder="璇疯緭鍏ュ彂鐢熸椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">缁撴潫鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="endTime" id="endTime$" placeholder="璇疯緭鍏ョ粨鏉熸椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鐘舵��: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="wrkSts" placeholder="璇疯緭鍏ュ伐浣滅姸鎬�" style="display: none">
-                        <input id="wrkSts$" name="wrkSts$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ュ伐浣滅姸鎬�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="basWrkStatusQueryBywrkSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="basWrkStatusQueryBywrkStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍏ュ嚭搴撶被鍨�: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="ioType" placeholder="璇疯緭鍏ュ叆鍑哄簱绫诲瀷" style="display: none">
-                        <input id="ioType$" name="ioType$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ュ叆鍑哄簱绫诲瀷" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="basWrkIotypeQueryByioType" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="basWrkIotypeQueryByioTypeSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">RGV鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="rgvNo" placeholder="璇疯緭鍏GV鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣搴撲綅: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="locNo" placeholder="璇疯緭鍏ョ洰鏍囧簱浣�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣绔�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="staNo" placeholder="璇疯緭鍏ョ洰鏍囩珯">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">婧愮珯: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceStaNo" placeholder="璇疯緭鍏ユ簮绔�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">婧愬簱浣�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceLocNo" placeholder="璇疯緭鍏ユ簮搴撲綅">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鏉$爜: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="barcode" placeholder="璇疯緭鍏ユ潯鐮�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鐮�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="errCode" placeholder="璇疯緭鍏ュ紓甯哥爜">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="error" placeholder="璇疯緭鍏ュ紓甯�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">寮傚父鎯呭喌: </label>
-                    <div class="layui-input-block">
-                        <select name="status">
-                            <option value="">璇烽�夋嫨寮傚父鎯呭喌</option>
-                            <option value="1">鏈鐞�</option>
-                            <option value="2">宸蹭慨澶�</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="createTime" id="createTime$" placeholder="璇疯緭鍏ユ坊鍔犳椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娣诲姞浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="createBy" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" style="display: none">
-                        <input id="createBy$" name="createBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ユ坊鍔犱汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryBycreateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryBycreateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" style="display: none">
-                        <input id="updateBy$" name="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绯荤粺鐘舵�佹暟鎹�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="systemStatus" placeholder="璇疯緭鍏ョ郴缁熺姸鎬佹暟鎹�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basRgvErrLog/basRgvErrLog_detail.html b/src/main/webapp/views/basRgvErrLog/basRgvErrLog_detail.html
deleted file mode 100644
index f1bccae..0000000
--- a/src/main/webapp/views/basRgvErrLog/basRgvErrLog_detail.html
+++ /dev/null
@@ -1,202 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basRgvErrLog')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="taskNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍙戠敓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="startTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">缁撴潫鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="endTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸ヤ綔鐘舵�侊細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="wrkSts" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="wrkSts$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basWrkStatusQueryBywrkSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basWrkStatusQueryBywrkStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍏ュ嚭搴撶被鍨嬶細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="ioType" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="ioType$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basWrkIotypeQueryByioType" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basWrkIotypeQueryByioTypeSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">RGV鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="rgvNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩爣搴撲綅锛�</label>
-            <div class="layui-input-inline">
-                <input id="locNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩� 鏍� 绔欙細</label>
-            <div class="layui-input-inline">
-                <input id="staNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婧愩��銆�绔欙細</label>
-            <div class="layui-input-inline">
-                <input id="sourceStaNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婧� 搴� 浣嶏細</label>
-            <div class="layui-input-inline">
-                <input id="sourceLocNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鏉°��銆�鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="barcode" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮� 甯� 鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="errCode" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮傘��銆�甯革細</label>
-            <div class="layui-input-inline">
-                <input id="error" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">寮傚父鎯呭喌锛�</label>
-            <div class="layui-input-inline">
-                <select id="status">
-                    <option value="" style="display: none"></option>
-                    <option value="1">鏈鐞�</option>
-                    <option value="2">宸蹭慨澶�</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">娣诲姞浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="createBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="createBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryBycreateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryBycreateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">绯荤粺鐘舵�佹暟鎹細</label>
-            <div class="layui-input-inline">
-                <input id="systemStatus" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basRgvErrLog/basRgvErrLog.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basRgvOpt/basRgvOpt.html b/src/main/webapp/views/basRgvOpt/basRgvOpt.html
index b96e78a..29b7ff6 100644
--- a/src/main/webapp/views/basRgvOpt/basRgvOpt.html
+++ b/src/main/webapp/views/basRgvOpt/basRgvOpt.html
@@ -1,174 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasRgvOpt 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basRgvOpt" lay-filter="basRgvOpt"></table>
-        </div>
-    </div>
-</div>
 
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-<!--        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>-->
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basRgvOpt/basRgvOpt.js" charset="utf-8"></script>
-</body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="taskNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">RGV鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="rgvNo" placeholder="璇疯緭鍏GV鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">涓嬪彂鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sendTime" id="sendTime$" placeholder="璇疯緭鍏ヤ笅鍙戞椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">浣滀笟: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="mode" placeholder="璇疯緭鍏ヤ綔涓�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璧风偣搴撲綅: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceLocNo" placeholder="璇疯緭鍏ヨ捣鐐瑰簱浣�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣搴撲綅: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="targetLocNo" placeholder="璇疯緭鍏ョ洰鏍囧簱浣�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" style="display: none">
-                        <input id="updateBy$" name="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
+            <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>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍛戒护: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="command" placeholder="璇疯緭鍏ュ懡浠�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绯荤粺鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="systemStatus" placeholder="璇疯緭鍏ョ郴缁熺姸鎬�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">涓嬪彂鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="send">
-                            <option value="">璇烽�夋嫨涓嬪彂鐘舵��</option>
-                            <option value="0">鏈笅鍙�</option>
-                            <option value="1">宸蹭笅鍙�</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璇锋眰鍝嶅簲: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="response" placeholder="璇疯緭鍏ヨ姹傚搷搴�">
-                    </div>
-                </div>
+            </el-collapse-transition>
 
-             </div>
+            <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>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasRgvOpt' : '淇敼 BasRgvOpt'"
+        :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>
-    </form>
-</script>
+    </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/basRgvOpt/basRgvOpt.js?v=20260310" charset="utf-8"></script>
+</body>
 </html>
-
diff --git a/src/main/webapp/views/basRgvOpt/basRgvOpt_detail.html b/src/main/webapp/views/basRgvOpt/basRgvOpt_detail.html
deleted file mode 100644
index 3405f4c..0000000
--- a/src/main/webapp/views/basRgvOpt/basRgvOpt_detail.html
+++ /dev/null
@@ -1,142 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basRgvOpt')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="taskNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">RGV鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="rgvNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">涓嬪彂鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="sendTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">浣溿��銆�涓氾細</label>
-            <div class="layui-input-inline">
-                <input id="mode" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璧风偣搴撲綅锛�</label>
-            <div class="layui-input-inline">
-                <input id="sourceLocNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩爣搴撲綅锛�</label>
-            <div class="layui-input-inline">
-                <input id="targetLocNo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍛姐��銆�浠わ細</label>
-            <div class="layui-input-inline">
-                <input id="command" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">绯荤粺鐘舵�侊細</label>
-            <div class="layui-input-inline">
-                <input id="systemStatus" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">涓嬪彂鐘舵�侊細</label>
-            <div class="layui-input-inline">
-                <select id="send">
-                    <option value="" style="display: none"></option>
-                    <option value="0">鏈笅鍙�</option>
-                    <option value="1">宸蹭笅鍙�</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璇锋眰鍝嶅簲锛�</label>
-            <div class="layui-input-inline">
-                <input id="response" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basRgvOpt/basRgvOpt.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basStation/basStation.html b/src/main/webapp/views/basStation/basStation.html
index 08134dd..9f3bb23 100644
--- a/src/main/webapp/views/basStation/basStation.html
+++ b/src/main/webapp/views/basStation/basStation.html
@@ -1,157 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasStation 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="station_id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basStation" lay-filter="basStation"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasStation' : '淇敼 BasStation'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basStation/basStation.js" charset="utf-8"></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/basStation/basStation.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="stationId" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="status">
-                            <option value="">璇烽�夋嫨鐘舵��</option>
-                            <option value="1">姝e父</option>
-                            <option value="0">绂佺敤</option>
-                        </select>
-                    </div>
-                </div>
-                <!-- <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="wrkNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div> -->
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍙叆(checkBox): </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="inEnable" placeholder="璇疯緭鍏ュ彲鍏�(checkBox)">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍙嚭(checkBox): </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="outEnable" placeholder="璇疯緭鍏ュ彲鍑�(checkBox)">
-                    </div>
-                </div>
-                <!-- <div class="layui-form-item">
-                    <label class="layui-form-label">鍒涘缓浜哄憳: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="createBy" placeholder="璇疯緭鍏ュ垱寤轰汉鍛�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍒涘缓鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="createTime" id="createTime$" placeholder="璇疯緭鍏ュ垱寤烘椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div> -->
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绔欑偣妤煎眰: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="stationLev" placeholder="璇疯緭鍏ョ珯鐐规ゼ灞�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璁惧缂栧彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="deviceNo" placeholder="璇疯緭鍏ヨ澶囩紪鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绔欑偣鍒悕: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="stationAlias" placeholder="璇疯緭鍏ョ珯鐐瑰埆鍚�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/basStation/basStation_detail.html b/src/main/webapp/views/basStation/basStation_detail.html
deleted file mode 100644
index e843aae..0000000
--- a/src/main/webapp/views/basStation/basStation_detail.html
+++ /dev/null
@@ -1,118 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="stationId" class="layui-input" type="text" onkeyup="check(this.id, 'basStation')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐘躲��銆�鎬侊細</label>
-            <div class="layui-input-inline">
-                <select id="status">
-                    <option value="" style="display: none"></option>
-                    <option value="1">姝e父</option>
-                    <option value="0">绂佺敤</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="wrkNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鍙叆(checkBox)锛�</label>
-            <div class="layui-input-inline">
-                <input id="inEnable" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鍙嚭(checkBox)锛�</label>
-            <div class="layui-input-inline">
-                <input id="outEnable" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="createBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">绔欑偣妤煎眰锛�</label>
-            <div class="layui-input-inline">
-                <input id="stationLev" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basStation/basStation.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basStationOpt/basStationOpt.html b/src/main/webapp/views/basStationOpt/basStationOpt.html
index 042573c..f65e103 100644
--- a/src/main/webapp/views/basStationOpt/basStationOpt.html
+++ b/src/main/webapp/views/basStationOpt/basStationOpt.html
@@ -1,174 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasStationOpt 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="task_no" placeholder="宸ヤ綔鍙�" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="basStationOpt" lay-filter="basStationOpt"></table>
-        </div>
-    </div>
-</div>
 
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basStationOpt/basStationOpt.js" charset="utf-8"></script>
-</body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="taskNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绔欑偣缂栧彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="stationId" placeholder="璇疯緭鍏ョ珯鐐圭紪鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">涓嬪彂鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sendTime" id="sendTime$" placeholder="璇疯緭鍏ヤ笅鍙戞椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">浣滀笟: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="mode" placeholder="璇疯緭鍏ヤ綔涓�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">婧愮珯鐐�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="sourceStationId" placeholder="璇疯緭鍏ユ簮绔欑偣">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鐩爣绔欑偣: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="targetStationId" placeholder="璇疯緭鍏ョ洰鏍囩珯鐐�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼鏃堕棿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="updateTime" id="updateTime$" placeholder="璇疯緭鍏ヤ慨鏀规椂闂�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">淇敼浜哄憳: </label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input class="layui-input" name="updateBy" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" style="display: none">
-                        <input id="updateBy$" name="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏ヤ慨鏀逛汉鍛�" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
+            <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>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">澶囨敞: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="memo" placeholder="璇疯緭鍏ュ娉�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍛戒护: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="command" placeholder="璇疯緭鍏ュ懡浠�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">绯荤粺鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="systemStatus" placeholder="璇疯緭鍏ョ郴缁熺姸鎬�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">涓嬪彂鐘舵��: </label>
-                    <div class="layui-input-block">
-                        <select name="send">
-                            <option value="">璇烽�夋嫨涓嬪彂鐘舵��</option>
-                            <option value="0">鏈笅鍙�</option>
-                            <option value="1">宸蹭笅鍙�</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璇锋眰鍝嶅簲: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="response" placeholder="璇疯緭鍏ヨ姹傚搷搴�">
-                    </div>
-                </div>
+            </el-collapse-transition>
 
-             </div>
+            <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>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasStationOpt' : '淇敼 BasStationOpt'"
+        :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>
-    </form>
-</script>
+    </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/basStationOpt/basStationOpt.js?v=20260310" charset="utf-8"></script>
+</body>
 </html>
-
diff --git a/src/main/webapp/views/basStationOpt/basStationOpt_detail.html b/src/main/webapp/views/basStationOpt/basStationOpt_detail.html
deleted file mode 100644
index 2e472a1..0000000
--- a/src/main/webapp/views/basStationOpt/basStationOpt_detail.html
+++ /dev/null
@@ -1,142 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'basStationOpt')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="taskNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">绔欑偣缂栧彿锛�</label>
-            <div class="layui-input-inline">
-                <input id="stationId" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">涓嬪彂鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="sendTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">浣溿��銆�涓氾細</label>
-            <div class="layui-input-inline">
-                <input id="mode" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婧� 绔� 鐐癸細</label>
-            <div class="layui-input-inline">
-                <input id="sourceStationId" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩爣绔欑偣锛�</label>
-            <div class="layui-input-inline">
-                <input id="targetStationId" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="updateTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="updateBy" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="updateBy$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByupdateBy" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByupdateBySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memo" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍛姐��銆�浠わ細</label>
-            <div class="layui-input-inline">
-                <input id="command" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">绯荤粺鐘舵�侊細</label>
-            <div class="layui-input-inline">
-                <input id="systemStatus" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">涓嬪彂鐘舵�侊細</label>
-            <div class="layui-input-inline">
-                <select id="send">
-                    <option value="" style="display: none"></option>
-                    <option value="0">鏈笅鍙�</option>
-                    <option value="1">宸蹭笅鍙�</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璇锋眰鍝嶅簲锛�</label>
-            <div class="layui-input-inline">
-                <input id="response" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basStationOpt/basStationOpt.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basWrkIotype/basWrkIotype.html b/src/main/webapp/views/basWrkIotype/basWrkIotype.html
index af2b770..abfb239 100644
--- a/src/main/webapp/views/basWrkIotype/basWrkIotype.html
+++ b/src/main/webapp/views/basWrkIotype/basWrkIotype.html
@@ -1,54 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasWrkIotype 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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>
 
-<!-- 鎼滅储鏍� -->
-<div id="search-box" class="layui-form layui-card-header">
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="io_type" placeholder="鍏ュ嚭绫诲瀷浠e彿" autocomplete="off">
+            <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>
-    </div>
+    </section>
 
-    <!-- 寰呮坊鍔� -->
-    <div id="data-search-btn" class="layui-btn-container layui-form-item" style="display: inline-block">
-        <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>
-        <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>
-    </div>
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasWrkIotype' : '淇敼 BasWrkIotype'"
+        :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>
 
-<!-- 琛ㄦ牸 -->
-<table class="layui-hide" id="basWrkIotype" lay-filter="basWrkIotype"></table>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basWrkIotype/basWrkIotype.js" charset="utf-8"></script>
-
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/basWrkIotype/basWrkIotype.js?v=20260310" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/basWrkIotype/basWrkIotype_detail.html b/src/main/webapp/views/basWrkIotype/basWrkIotype_detail.html
deleted file mode 100644
index 0c10693..0000000
--- a/src/main/webapp/views/basWrkIotype/basWrkIotype_detail.html
+++ /dev/null
@@ -1,94 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form" style="text-align: center">
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label" style="font-size: x-small"><span class="not-null">*</span>鍏ュ嚭绫诲瀷浠e彿锛�</label>
-            <div class="layui-input-inline">
-                <input id="ioType" class="layui-input" type="text" onkeyup="check(this.id, 'basWrkIotype')"  lay-verify="required|number">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>涓汇��銆�瑕侊細</label>
-            <div class="layui-input-inline">
-                <input id="ioPri" class="layui-input" type="text" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label" style="font-size: x-small">鍏ュ嚭绫诲瀷鎻忚堪锛�</label>
-            <div class="layui-input-inline">
-                <input id="ioDesc" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="modiUser" class="layui-input" type="text" style="display: none">
-                <input id="modiUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryBymodiUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryBymodiUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="modiTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">鍒� 寤� 鑰咃細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="appeUser" class="layui-input" type="text" style="display: none">
-                <input id="appeUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByappeUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByappeUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="appeTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basWrkIotype/basWrkIotype.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/basWrkStatus/basWrkStatus.html b/src/main/webapp/views/basWrkStatus/basWrkStatus.html
index 2b82780..eb9220b 100644
--- a/src/main/webapp/views/basWrkStatus/basWrkStatus.html
+++ b/src/main/webapp/views/basWrkStatus/basWrkStatus.html
@@ -1,54 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>BasWrkStatus 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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>
 
-<!-- 鎼滅储鏍� -->
-<div id="search-box" class="layui-form layui-card-header">
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="wrk_sts" placeholder="浠e彿" autocomplete="off">
+            <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>
-    </div>
+    </section>
 
-    <!-- 寰呮坊鍔� -->
-    <div id="data-search-btn" class="layui-btn-container layui-form-item" style="display: inline-block">
-        <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>
-        <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>
-    </div>
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 BasWrkStatus' : '淇敼 BasWrkStatus'"
+        :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>
 
-<!-- 琛ㄦ牸 -->
-<table class="layui-hide" id="basWrkStatus" lay-filter="basWrkStatus"></table>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basWrkStatus/basWrkStatus.js" charset="utf-8"></script>
-
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/basWrkStatus/basWrkStatus.js?v=20260310" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/basWrkStatus/basWrkStatus_detail.html b/src/main/webapp/views/basWrkStatus/basWrkStatus_detail.html
deleted file mode 100644
index db48c4b..0000000
--- a/src/main/webapp/views/basWrkStatus/basWrkStatus_detail.html
+++ /dev/null
@@ -1,96 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form" style="text-align: center">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label"><span class="not-null">*</span>浠c��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="wrkSts" class="layui-input" type="text" onkeyup="check(this.id, 'basWrkStatus')"  lay-verify="required|number">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鐘舵�佹弿杩帮細</label>
-            <div class="layui-input-inline">
-                <input id="wrkDesc" class="layui-input" type="text"  lay-verify="required">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">淇敼浜哄憳锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="modiUser" class="layui-input" type="text" style="display: none">
-                <input id="modiUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryBymodiUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryBymodiUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">淇敼鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="modiTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">鍒� 寤� 鑰咃細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="appeUser" class="layui-input" type="text" style="display: none">
-                <input id="appeUser$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQueryByappeUser" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQueryByappeUserSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label">娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="appeTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/basWrkStatus/basWrkStatus.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/config/config.html b/src/main/webapp/views/config/config.html
index 0d878ba..02587dd 100644
--- a/src/main/webapp/views/config/config.html
+++ b/src/main/webapp/views/config/config.html
@@ -1,67 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
-
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>Config 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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>
 
-    <!-- 鎼滅储鏍� -->
-    <div id="search-box" class="layui-form layui-card-header">
-        <div class="layui-inline">
-            <div class="layui-input-inline">
-                <input class="layui-input" type="text" name="code" placeholder="缂栫爜" autocomplete="off">
+            <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>
-        <div class="layui-inline">
-            <div class="layui-input-inline">
-                <select name="select_type" id="selectTypeSearch" lay-search="">
-                    <option value="">绛涢�夌被鍨�</option>
-                </select>
-            </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 Config' : '淇敼 Config'"
+        :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>
 
-        <!-- 寰呮坊鍔� -->
-        <div id="data-search-btn" class="layui-btn-container layui-form-item">
-            <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit
-                lay-filter="search">鎼滅储</button>
-            <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit
-                lay-filter="reset">閲嶇疆</button>
-        </div>
-    </div>
-
-    <!-- 琛ㄦ牸 -->
-    <table class="layui-hide" id="config" lay-filter="config"></table>
-    <script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">瀵煎嚭</button>
-        <button class="layui-btn layui-btn-warm layui-btn-sm" id="btn-refresh-cache" lay-event="refreshCache">鍒锋柊缂撳瓨</button>
-    </div>
-</script>
-
-    <script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">璇︽儏</a>
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-</script>
-
-    <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-    <script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-    <script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-    <script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-    <script type="text/javascript" src="../../static/js/config/config.js" charset="utf-8"></script>
-
-    <iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/config/config.js?v=20260310" charset="utf-8"></script>
 </body>
-
 </html>
diff --git a/src/main/webapp/views/config/config_detail.html b/src/main/webapp/views/config/config_detail.html
deleted file mode 100644
index 729d80c..0000000
--- a/src/main/webapp/views/config/config_detail.html
+++ /dev/null
@@ -1,92 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-
-<body>
-
-    <!-- 璇︽儏 -->
-    <div id="data-detail" class="layer_self_wrap">
-        <form id="detail" class="layui-form">
-            <div class="layui-inline" style="display: none">
-                <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-                <div class="layui-input-inline">
-                    <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-                </div>
-            </div>
-            <div class="layui-inline" style="width:31%;">
-                <label class="layui-form-label"><span class="not-null">*</span>鍚嶃��銆�绉帮細</label>
-                <div class="layui-input-inline">
-                    <input id="name" class="layui-input" type="text" placeholder="鍚嶇О" lay-verify="required">
-                </div>
-            </div>
-            <div class="layui-inline" style="width:31%;">
-                <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鐮侊細</label>
-                <div class="layui-input-inline">
-                    <input id="code" class="layui-input" type="text" placeholder="缂栫爜" lay-verify="required">
-                </div>
-            </div>
-            <div class="layui-inline" style="width:97%;">
-                <label class="layui-form-label"><span class="not-null">*</span>瀵� 搴� 鍊硷細</label>
-                <div class="layui-input-inline">
-                    <input id="value" class="layui-input" type="text" placeholder="瀵瑰簲鍊�" lay-verify="required">
-                </div>
-            </div>
-            <div class="layui-inline" style="width:31%;">
-                <label class="layui-form-label"><span class="not-null">*</span>绫汇��銆�鍨嬶細</label>
-                <div class="layui-input-inline">
-                    <select id="type" lay-verify="required">
-                        <option value="" style="display: none"></option>
-                        <option value="1">String</option>
-                        <option value="2">JSON</option>
-                    </select>
-                </div>
-            </div>
-            <div class="layui-inline" style="width:31%;">
-                <label class="layui-form-label"><span class="not-null">*</span>鐘躲��銆�鎬侊細</label>
-                <div class="layui-input-inline">
-                    <select id="status" lay-verify="required">
-                        <option value="" style="display: none"></option>
-                        <option value="1">姝e父</option>
-                        <option value="0">绂佺敤</option>
-                    </select>
-                </div>
-            </div>
-            <div class="layui-inline" style="width:97%;">
-                <label class="layui-form-label">绛涢�夌被鍨嬶細</label>
-                <div class="layui-input-inline">
-                    <input id="selectType" class="layui-input" type="text" placeholder="绛涢�夌被鍨�">
-                </div>
-            </div>
-
-
-            <hr class="layui-bg-gray">
-
-            <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-                <div id="data-detail-submit" type="button" class="layui-btn layui-btn-normal" lay-submit
-                    lay-filter="edit">淇濆瓨</div>
-                <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-            </div>
-
-            <div id="prompt">
-                娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-            </div>
-        </form>
-    </div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/config/config.js" charset="utf-8"></script>
-
-</html>
\ No newline at end of file
diff --git a/src/main/webapp/views/detail.html b/src/main/webapp/views/detail.html
index 9a7739c..e62b4ae 100644
--- a/src/main/webapp/views/detail.html
+++ b/src/main/webapp/views/detail.html
@@ -1,172 +1,282 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="UTF-8">
-    <title></title>
+    <title>璁剧疆鎴戠殑璧勬枡</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/layui/css/layui.css" media="all">
-    <script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></script>
+    <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.96);
+            --card-border: rgba(216, 226, 238, 0.96);
+            --text-main: #243447;
+        }
+
+        [v-cloak] {
+            display: none;
+        }
+
+        html,
         body {
-            background-color: #fff;
+            margin: 0;
+            min-height: 100%;
+            color: var(--text-main);
+            font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
+            background: #f5f7fb;
         }
-        #container {
-            padding: 15px;
-            margin-top: 15px
+
+        .page-shell {
+            padding: 14px 16px;
+            box-sizing: border-box;
         }
-        .layui-card-body {
-            padding: 20px 15px 5px 15px;
+
+        .card-shell {
+            max-width: 860px;
+            margin: 0 auto;
+            border-radius: 16px;
+            border: 1px solid var(--card-border);
+            background: var(--card-bg);
+            box-shadow: 0 8px 20px rgba(44, 67, 96, 0.05);
+            overflow: hidden;
         }
-        .layui-form-item {
-            margin-bottom: 18px;
+
+        .card-head {
+            padding: 16px 20px 12px;
+            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
+            background: #f8fbff;
         }
-        .layui-form-label {
-            color: #999!important;;
-            width: 60px;
+
+        .card-title {
+            font-size: 16px;
+            font-weight: 700;
+            color: var(--text-main);
         }
-        .layui-input-block {
-            margin-left: 90px;
+
+        .card-body {
+            padding: 18px 20px 16px;
         }
-        #update-password {
-            padding: 0 20px 0 15px;
+
+        .form-shell {
+            max-width: 740px;
+            margin: 0 auto;
         }
-        .layui-btn .layui-icon {
-            margin-right: 0;
+
+        .profile-form .el-form-item {
+            margin-bottom: 14px;
+        }
+
+        .profile-form .el-form-item__label {
+            font-weight: 600;
+            color: #5c6f82;
+        }
+
+        .profile-form .el-input__inner,
+        .profile-form .el-button {
+            height: 34px;
+            line-height: 34px;
+        }
+
+        .profile-form .el-button {
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+            padding: 0 16px;
+            vertical-align: middle;
+        }
+
+        .profile-form .el-button [class^="el-icon-"],
+        .profile-form .el-button [class*=" el-icon-"] {
+            line-height: 1;
+            margin-right: 6px;
+        }
+
+        .password-row {
+            display: flex;
+            align-items: center;
+            gap: 10px;
+        }
+
+        .password-mask {
+            width: 100%;
+        }
+
+        .password-row .el-button {
+            min-width: 118px;
+            flex: 0 0 118px;
+        }
+
+        .footer-bar {
+            display: flex;
+            justify-content: flex-end;
+            padding-top: 4px;
+        }
+
+        .footer-bar .el-button {
+            min-width: 118px;
+        }
+
+        .password-dialog .el-dialog {
+            border-radius: 18px;
+            overflow: hidden;
+        }
+
+        .password-dialog .el-dialog__header {
+            padding: 18px 20px 12px;
+            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
+            background: #f8fbff;
+        }
+
+        .password-dialog .el-dialog__title {
+            font-weight: 700;
+            color: var(--text-main);
+        }
+
+        .password-dialog .el-dialog__body {
+            padding: 18px 20px 12px;
+        }
+
+        .password-form .el-form-item {
+            margin-bottom: 16px;
+        }
+
+        .password-form .el-form-item__label {
+            font-weight: 600;
+            color: #5c6f82;
+            white-space: nowrap;
+        }
+
+        .password-form .el-input__inner,
+        .password-form .el-button {
+            height: 34px;
+            line-height: 34px;
+        }
+
+        .password-form .el-button {
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+            min-width: 120px;
+            padding: 0 18px;
+        }
+
+        .password-footer {
+            display: flex;
+            justify-content: center;
+            gap: 12px;
+            padding-top: 4px;
+        }
+
+        @media (max-width: 768px) {
+            .page-shell {
+                padding: 10px;
+            }
+
+            .password-row {
+                flex-direction: column;
+                align-items: stretch;
+            }
+
+            .form-shell {
+                max-width: none;
+            }
         }
     </style>
 </head>
-<body id="body">
-<div id="container">
-    <fieldset class="layui-elem-field">
-        <legend>璁剧疆鎴戠殑璧勬枡</legend>
-        <div class="layui-card-body">
-            <div id="person-detail" class="layui-form">
-                <div class="layui-form-item" style="display: none">
-                    <label class="layui-form-label">缂栧彿</label>
-                    <div class="layui-input-inline">
-                        <input id="id" class="layui-input" type="text" placeholder="璇疯緭鍏�" autocomplete="off">
+<body>
+<div id="app" class="page-shell" v-cloak>
+    <section class="card-shell">
+        <div class="card-head">
+            <div class="card-title">璁剧疆鎴戠殑璧勬枡</div>
+        </div>
+        <div class="card-body">
+            <div class="form-shell">
+                <input id="id" type="hidden" v-model="form.id">
+                <input id="password" type="hidden" v-model="form.password">
+                <el-form
+                    ref="profileForm"
+                    class="profile-form"
+                    :model="form"
+                    :rules="rules"
+                    label-width="78px"
+                    size="small"
+                    @submit.native.prevent>
+                    <el-row :gutter="16">
+                        <el-col :xs="24" :sm="12">
+                            <el-form-item label="瑙掕壊">
+                                <el-input id="roleName" v-model="form.roleName" disabled></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :xs="24" :sm="12">
+                            <el-form-item label="娉ㄥ唽鏃堕棿">
+                                <el-input v-model="form.createTime$" disabled></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :xs="24" :sm="12">
+                            <el-form-item label="鍚嶇О" prop="username">
+                                <el-input id="username" v-model.trim="form.username" maxlength="50" clearable placeholder="璇疯緭鍏�"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :xs="24" :sm="12">
+                            <el-form-item label="璐﹀彿" prop="mobile">
+                                <el-input id="mobile" v-model.trim="form.mobile" maxlength="50" clearable placeholder="璇疯緭鍏�"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :xs="24">
+                            <el-form-item label="瀵嗙爜">
+                                <div class="password-row">
+                                    <el-input class="password-mask" value="宸茶缃瘑鐮�" disabled></el-input>
+                                    <el-button type="primary" plain icon="el-icon-lock" @click="openPasswordDialog">淇敼瀵嗙爜</el-button>
+                                </div>
+                            </el-form-item>
+                        </el-col>
+                    </el-row>
+                    <div class="footer-bar">
+                        <el-button type="primary" :loading="saving" @click="handleSave">纭淇敼</el-button>
                     </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">瑙掕壊</label>
-                    <div class="layui-input-inline">
-                        <input id="roleName" class="layui-input" type="text" placeholder="璇疯緭鍏�" autocomplete="off" disabled="disabled">
-                    </div>
-                    <div class="layui-form-mid layui-word-aux">褰撳墠瑙掕壊涓嶅彲鏇存敼涓哄叾瀹冭鑹�</div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鍚嶇О</label>
-                    <div class="layui-input-inline">
-                        <input id="username" class="layui-input" type="text" placeholder="璇疯緭鍏�" autocomplete="off">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璐﹀彿</label>
-                    <div class="layui-input-inline">
-                        <input id="mobile" class="layui-input" type="text" placeholder="璇疯緭鍏�" autocomplete="off">
-                    </div>
-                    <div class="layui-form-mid layui-word-aux">閲嶈锛佷竴鑸敤浜庡悗鍙扮櫥鍏�</div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">瀵嗙爜</label>
-                    <div class="layui-input-inline">
-                        <input id="password" class="layui-input" type="text" placeholder="璇疯緭鍏�" autocomplete="off" style="display: none">
-                        <button id="update-password" class="layui-btn layui-btn-primary">
-                            <i class="layui-icon">&#xe620;</i>
-                            淇敼瀵嗙爜
-                        </button>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">娉ㄥ唽鏃堕棿</label>
-                    <div class="layui-input-inline">
-                        <input id="createTime$" class="layui-input" type="text" placeholder="璇疯緭鍏�" autocomplete="off" disabled="disabled">
-                    </div>
-                    <div class="layui-form-mid layui-word-aux">涓嶅彲淇敼</div>
-                </div>
-                <div class="layui-form-item">
-                    <div class="layui-input-block">
-                        <button type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">纭淇敼</button>
-                    </div>
-                </div>
+                </el-form>
             </div>
         </div>
-    </fieldset>
+    </section>
+
+    <el-dialog
+        class="password-dialog"
+        title="淇敼瀵嗙爜"
+        :visible.sync="passwordDialogVisible"
+        width="420px"
+        :close-on-click-modal="false"
+        append-to-body>
+        <el-form
+            ref="passwordForm"
+            class="password-form"
+            :model="passwordForm"
+            :rules="passwordRules"
+            label-width="112px"
+            size="small"
+            @submit.native.prevent>
+            <el-form-item label="褰撳墠瀵嗙爜" prop="oldPassword">
+                <el-input v-model="passwordForm.oldPassword" type="password" show-password autocomplete="off"></el-input>
+            </el-form-item>
+            <el-form-item label="鏂板瘑鐮�" prop="password">
+                <el-input v-model="passwordForm.password" type="password" show-password autocomplete="off"></el-input>
+            </el-form-item>
+            <el-form-item label="纭鏂板瘑鐮�" prop="rePassword">
+                <el-input v-model="passwordForm.rePassword" type="password" show-password autocomplete="off"></el-input>
+            </el-form-item>
+            <div class="password-footer">
+                <el-button @click="closePasswordDialog">鍏抽棴</el-button>
+                <el-button type="primary" :loading="passwordSaving" @click="handlePasswordSave">淇濆瓨</el-button>
+            </div>
+        </el-form>
+    </el-dialog>
 </div>
 </body>
 <script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script src="../static/layui/layui.js"></script>
-<script>
-    layui.use(['form'], function(){
-        var form = layui.form,
-            layer = layui.layer,
-            $ = layui.jquery;
-
-        // 鍒濆鍖栬〃鍗曟暟鎹�
-        $.ajax({
-            url: baseUrl+"/user/detail/auth",
-            headers: {'token': localStorage.getItem('token')},
-            method: 'POST',
-            success: function (res) {
-                if (res.code === 200){
-                    var user = res.data;
-                    for (var val in user) {
-                        $('#person-detail').find(":input[id='" + val + "']").val(user[val]);
-                    }
-                } else if (res.code === 403){
-                    top.location.href = baseUrl+"/";
-                } else {
-                    layer.msg(res.msg);
-                }
-            }
-        });
-
-        // 淇敼瀵嗙爜
-        $(document).on('click','#update-password', function () {
-            layer.open({
-                type: 2,
-                title: '淇敼瀵嗙爜',
-                maxmin: true,
-                area: ['350px', '310px'],
-                shadeClose: false,
-                content: 'password.html',
-                success: function(layero, index){
-                    layer.iframeAuto(index);
-                }
-            })
-        });
-
-
-        // 淇濆瓨淇敼
-        form.on('submit(save)', function (data) {
-            var user = {
-                id: $('#id').val(),
-                username: $('#username').val(),
-                mobile: $("#mobile").val(),
-            };
-            layer.confirm('纭畾淇敼璧勬枡鍚楋紵', function(){
-                $.ajax({
-                    url: baseUrl+"/user/update/auth",
-                    headers: {'token': localStorage.getItem('token')},
-                    data: user,
-                    method: 'POST',
-                    success: function (res) {
-                        if (res.code === 200){
-                            layer.msg(res.msg);
-                            localStorage.setItem("username", user.username);
-                            parent.$('#person-username').text(localStorage.getItem('username'));
-                        } else if (res.code === 403){
-                            top.location.href = baseUrl+"/";
-                        } else {
-                            layer.msg(res.msg);
-                        }
-                    }
-                });
-            });
-            return false;
-        });
-    });
-</script>
-</html>
\ No newline at end of file
+<script type="text/javascript" src="../static/js/tools/md5.js"></script>
+<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></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/detail/detail.js?v=20260310_detail_vue3"></script>
+</html>
diff --git a/src/main/webapp/views/deviceConfig/deviceConfig.html b/src/main/webapp/views/deviceConfig/deviceConfig.html
index 7818180..2635863 100644
--- a/src/main/webapp/views/deviceConfig/deviceConfig.html
+++ b/src/main/webapp/views/deviceConfig/deviceConfig.html
@@ -1,138 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>DeviceConfig 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="deviceConfig" lay-filter="deviceConfig"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 DeviceConfig' : '淇敼 DeviceConfig'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/deviceConfig/deviceConfig.js" charset="utf-8"></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/deviceConfig/deviceConfig.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璁惧缂栧彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="deviceNo" placeholder="璇疯緭鍏ヨ澶囩紪鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璁惧绫诲瀷: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="deviceType" placeholder="璇疯緭鍏ヨ澶囩被鍨�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璁惧ip: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="ip" placeholder="璇疯緭鍏ヨ澶噄p">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">璁惧绔彛: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="port" placeholder="璇疯緭鍏ヨ澶囩鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">瀹炵幇绫�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="threadImpl" placeholder="璇疯緭鍏ュ疄鐜扮被">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">缃戝叧缂栧彿: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="gatewayId" placeholder="璇疯緭鍏ョ綉鍏崇紪鍙�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">铏氭嫙璁惧: </label>
-                    <div class="layui-input-block">
-                        <select name="fake">
-                            <option value="">璇烽�夋嫨鐘舵��</option>
-                            <option value="1">鏄�</option>
-                            <option value="0">鍚�</option>
-                        </select>
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">铏氭嫙璁惧鍒濆鍖栬澶囩姸鎬�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="fakeInitStatus" placeholder="璇疯緭鍏ヨ櫄鎷熻澶囧垵濮嬪寲璁惧鐘舵��">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/deviceConfig/deviceConfig_detail.html b/src/main/webapp/views/deviceConfig/deviceConfig_detail.html
deleted file mode 100644
index aa8fdef..0000000
--- a/src/main/webapp/views/deviceConfig/deviceConfig_detail.html
+++ /dev/null
@@ -1,90 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>锛�</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'deviceConfig')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璁惧ip锛�</label>
-            <div class="layui-input-inline">
-                <input id="ip" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璁惧绔彛锛�</label>
-            <div class="layui-input-inline">
-                <input id="port" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">瀹� 鐜� 绫伙細</label>
-            <div class="layui-input-inline">
-                <input id="threadImpl" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍒涘缓鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璁惧绫诲瀷锛�</label>
-            <div class="layui-input-inline">
-                <input id="deviceType" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">璁惧缂栧彿锛�</label>
-            <div class="layui-input-inline">
-                <input id="deviceNo" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/deviceConfig/deviceConfig.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/httpRequestLog/httpRequestLog.html b/src/main/webapp/views/httpRequestLog/httpRequestLog.html
index 0e4c5ad..d15af71 100644
--- a/src/main/webapp/views/httpRequestLog/httpRequestLog.html
+++ b/src/main/webapp/views/httpRequestLog/httpRequestLog.html
@@ -1,119 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>HttpRequestLog 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+    <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 class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <div class="layui-form toolbar" id="search-box">
-                <div class="layui-form-item">
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
+<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="layui-inline" style="width: 300px">
-                        <div class="layui-input-inline">
-                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-                        </div>
-                    </div>
-                    <div class="layui-inline">
-                        <div class="layui-input-inline">
-                            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-                        </div>
-                    </div>
-                    <div class="layui-inline">&emsp;
-                        <button class="layui-btn icon-btn" lay-filter="search" lay-submit>
-                            <i class="layui-icon">&#xe615;</i>鎼滅储
-                        </button>
-                        <button class="layui-btn icon-btn" lay-filter="reset" lay-submit>
-                            <i class="layui-icon">&#xe666;</i>閲嶇疆
-                        </button>
+                    <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>
-            <table class="layui-hide" id="httpRequestLog" lay-filter="httpRequestLog"></table>
+
+            <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 HttpRequestLog' : '淇敼 HttpRequestLog'"
+        :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/html" id="toolbar">
-    <div class="layui-btn-container">
-<!--        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>-->
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="float: right">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-<!--    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>-->
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-edit" lay-event="del">鍒犻櫎</a>
-</script>
-
-<script type="text/html" id="resTpl">
-    <span name="settle"
-          {{# if( d.result === 1){ }}
-          class="layui-badge layui-badge-green" >鎴愬姛</span>
-    {{# }else { }}
-    class="layui-badge layui-badge-red" >澶辫触</span>
-    {{# } }}
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/httpRequestLog/httpRequestLog.js" charset="utf-8"></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/httpRequestLog/httpRequestLog.js?v=20260310" charset="utf-8"></script>
 </body>
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
-        <div class="layui-row">
-            <div class="layui-col-md12">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="name" placeholder="璇疯緭鍏�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="request" placeholder="璇疯緭鍏�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="response" placeholder="璇疯緭鍏�">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="createTime" id="createTime$" placeholder="璇疯緭鍏�">
-                    </div>
-                </div>
-
-             </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
 </html>
-
diff --git a/src/main/webapp/views/httpRequestLog/httpRequestLog_detail.html b/src/main/webapp/views/httpRequestLog/httpRequestLog_detail.html
deleted file mode 100644
index 3fa606a..0000000
--- a/src/main/webapp/views/httpRequestLog/httpRequestLog_detail.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">锛�</label>
-            <div class="layui-input-inline">
-                <input id="name" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">锛�</label>
-            <div class="layui-input-inline">
-                <input id="request" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">锛�</label>
-            <div class="layui-input-inline">
-                <input id="response" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/httpRequestLog/httpRequestLog.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/locMast/locMast.html b/src/main/webapp/views/locMast/locMast.html
index db2e085..477993f 100644
--- a/src/main/webapp/views/locMast/locMast.html
+++ b/src/main/webapp/views/locMast/locMast.html
@@ -1,176 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>LocMast 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <link rel="stylesheet" href="../../static/vue/element/element.css">
+    <link rel="stylesheet" href="../../static/css/cool.css">
     <style>
-        #btn-init {
-            display: none;
-        }
-        .layui-layer-page .layui-layer-content {
-            position: relative;
-            overflow: visible !important;
+        :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>
 
-<!-- 鎼滅储鏍� -->
-<div id="search-box" class="layui-form layui-card-header">
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="loc_no" placeholder="搴撲綅鍙�" autocomplete="off">
+            <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>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="loc_sts" placeholder="搴撲綅鐘舵��" autocomplete="off">
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 LocMast' : '淇敼 LocMast'"
+        :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>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="row1" placeholder="鎺�" autocomplete="off">
-        </div>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="bay1" placeholder="鍒�" autocomplete="off">
-        </div>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="lev1" placeholder="灞�" autocomplete="off">
-        </div>
-    </div>
-    <!-- 寰呮坊鍔� -->
-    <div id="data-search-btn" class="layui-btn-container layui-form-item" style="display: inline-block">
-        <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>
-        <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>
-    </div>
+    </el-dialog>
 </div>
-
-<!-- 琛ㄦ牸 -->
-<div class="layui-form">
-    <table class="layui-hide" id="locMast" lay-filter="locMast"></table>
-</div>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-<!--    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">璇︽儏</a>-->
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-</script>
 
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/locMast/locMast.js" charset="utf-8"></script>
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
-<!-- 閲嶇疆搴撲綅寮圭獥 -->
-<div id="resetLocDiv" style="margin: 20px 0 10px 30px; display: none">
-    <div class="layui-form layui-form-pane">
-        <!-- 鎺� -->
-        <div class="layui-form-item">
-            <div class="layui-inline">
-                <label class="layui-form-label">璧锋鎺�</label>
-                <div class="layui-input-inline" style="width: 100px;">
-                    <input type="text" name="startRow" autocomplete="off" class="layui-input" lay-verify="required|number">
-                </div>
-                <div class="layui-form-mid">-</div>
-                <div class="layui-input-inline" style="width: 100px;">
-                    <input type="text" name="endRow" autocomplete="off" class="layui-input" lay-verify="required|number">
-                </div>
-            </div>
-        </div>
-        <!-- 鍒� -->
-        <div class="layui-form-item">
-            <div class="layui-inline">
-                <label class="layui-form-label">璧锋鍒�</label>
-                <div class="layui-input-inline" style="width: 100px;">
-                    <input type="text" name="startBay" autocomplete="off" class="layui-input" lay-verify="required|number">
-                </div>
-                <div class="layui-form-mid">-</div>
-                <div class="layui-input-inline" style="width: 100px;">
-                    <input type="text" name="endBay" autocomplete="off" class="layui-input" lay-verify="required|number">
-                </div>
-            </div>
-        </div>
-        <!-- 灞� -->
-        <div class="layui-form-item">
-            <div class="layui-inline">
-                <label class="layui-form-label">璧锋灞�</label>
-                <div class="layui-input-inline" style="width: 100px;">
-                    <input type="text" name="startLev" autocomplete="off" class="layui-input" lay-verify="required|number">
-                </div>
-                <div class="layui-form-mid">-</div>
-                <div class="layui-input-inline" style="width: 100px;">
-                    <input type="text" name="endLev" autocomplete="off" class="layui-input" lay-verify="required|number">
-                </div>
-            </div>
-        </div>
-        <div class="layui-form-item">
-            <label class="layui-form-label">鍫嗗灈鏈烘暟閲�</label>
-            <div class="layui-input-inline">
-                <input type="text" name="crnAmount" lay-verify="required|number" autocomplete="off" class="layui-input">
-            </div>
-        </div>
-        <!-- 搴撲綅绫诲瀷 -->
-        <div class="layui-form-item">
-            <label class="layui-form-label">楂樹綆绫诲瀷</label>
-            <div class="layui-input-inline">
-                <select name="locType1">
-                    <option style="display: none"></option>
-                    <option value="0">鏈煡</option>
-                    <option value="1">浣庡簱浣�</option>
-                    <option value="2">楂樺簱浣�</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-form-item">
-            <label class="layui-form-label">瀹界獎绫诲瀷</label>
-            <div class="layui-input-inline">
-                <select name="locType2">
-                    <option style="display: none"></option>
-                    <option value="0">鏈煡</option>
-                    <option value="1">绐勫簱浣�</option>
-                    <option value="2">瀹藉簱浣�</option>
-                </select>
-            </div>
-        </div>
-        <div class="layui-form-item">
-            <label class="layui-form-label">杞婚噸绫诲瀷</label>
-            <div class="layui-input-inline">
-                <select name="locType3">
-                    <option style="display: none"></option>
-                    <option value="0">鏈煡</option>
-                    <option value="1">杞诲簱浣�</option>
-                    <option value="2">閲嶅簱浣�</option>
-                </select>
-            </div>
-        </div>
-        <div id="prompt" style="text-indent: 10px;">
-            <span class="not-null">鍒濆鍖栧簱浣嶅悗灏嗗垹闄ゅ簱瀛樻槑缁嗭紝璇疯皑鎱庢搷浣滐紒</span>
-        </div>
-        <!-- 鎸夐挳 -->
-        <div style="text-align: center; margin-top: 20px">
-            <button class="layui-btn layui-btn-radius layui-btn-normal" id="initDo" lay-submit lay-filter="initDo">纭畾</button>
-        </div>
-    </div>
-</div>
+<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/locMast/locMast.js?v=20260310" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/locMast/locMast_detail.html b/src/main/webapp/views/locMast/locMast_detail.html
deleted file mode 100644
index 8b2ba44..0000000
--- a/src/main/webapp/views/locMast/locMast_detail.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form" style="text-align: center">
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label"><span class="not-null">*</span>搴� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="locNo" class="layui-input" type="text" onkeyup="check(this.id, 'locMast')">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label"><span class="not-null">*</span>搴撲綅鐘舵�侊細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="locSts" class="layui-input" type="text" style="display: none">
-                <input id="locSts$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basLocStsQueryBylocSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basLocStsQueryBylocStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label">鎺掞細</label>
-            <div class="layui-input-inline">
-                <input id="row1" class="layui-input" type="text" autocomplete="off" disabled="disabled">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label">鍒楋細</label>
-            <div class="layui-input-inline">
-                <input id="bay1" class="layui-input" type="text" autocomplete="off" disabled="disabled">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label">灞傦細</label>
-            <div class="layui-input-inline">
-                <input id="lev1" class="layui-input" type="text" autocomplete="off" disabled="disabled">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label">搴撲綅绫诲瀷锛�</label>
-            <div class="layui-input-inline">
-                <input id="locType" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/locMast/locMast.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/login.html b/src/main/webapp/views/login.html
index 640e406..01e5ad2 100644
--- a/src/main/webapp/views/login.html
+++ b/src/main/webapp/views/login.html
@@ -1,421 +1,524 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="UTF-8">
     <title>绯荤粺鐧诲綍</title>
-    <link rel="stylesheet" href="../static/layui/css/layui.css" media="all">
-    <link rel="icon" type="image/x-icon" href="../static/images/wcs_logo.png" />
+    <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="icon" type="image/x-icon" href="../static/images/wcs_logo.png">
+    <link rel="stylesheet" href="../static/vue/element/element.css">
     <link rel="stylesheet" href="../static/css/animate.min.css">
-    <script type="text/javascript" src="../static/layui/layui.js"></script>
-    <script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></script>
     <style>
-        html{
-            height: 100%;
-        }
-        body.login-bg {
-            color: #777;
-            height: 100%;
-            /*background-image: linear-gradient(to right, #ff9569 0%, #e92758 100%);*/
-            /*background-image: linear-gradient(-90deg, #a2e7f3 0%, #98baee 100%);*/
-            background-image: url("../static/images/login.png");
-            /*background: -webkit-linear-gradient(red, blue); !* Safari 5.1 - 6.0 *!;*/
-            /*background-image: url("../static/image/login_bg.jpg");*/
-            /*background-repeat: no-repeat;*/
-            /*background-size: cover;*/
-            /*background-position: top center;*/
+        [v-cloak] {
+            display: none;
         }
 
-        #login-wrapper {
-            box-sizing:border-box;
-            background: #fff;
-            position: absolute;
-            top: 45%;
-            left: 50%;
-            margin-top: -210px;
-            margin-left: -220px;
-            width: 400px;
-            min-height: 380px;
-            padding: 50px;
-            text-align: center;
-            border-radius: 5px;
-            box-shadow: 0px 0px 10px rgb(168, 165, 165);
-            transform-origin: 50% 50%;
-            /*animation: loading 1s 0s forwards;*/
-            transform: rotateX(0deg);
+        html,
+        body {
+            min-height: 100%;
+            margin: 0;
+            font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
         }
 
-        #login-wrapper h2 {
-            color: rgba(64,158,255,0.9);
-            font-size: 26px;
-            font-weight: bold;
-            margin-bottom: 30px;
+        body {
+            background:
+                radial-gradient(900px 420px at 12% 14%, rgba(92, 176, 255, 0.20), transparent 60%),
+                radial-gradient(760px 360px at 86% 18%, rgba(54, 208, 177, 0.18), transparent 58%),
+                linear-gradient(135deg, rgba(6, 26, 58, 0.84), rgba(7, 38, 73, 0.64)),
+                url("../static/images/login.png") center center / cover no-repeat;
+            color: #243447;
         }
 
-        .layadmin-user-login-body .layui-form-item {
-            margin-bottom: 20px;
+        .login-shell {
+            min-height: 100vh;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            padding: 36px 16px;
+            box-sizing: border-box;
             position: relative;
         }
-        .layadmin-user-login-body .layui-form-item .layui-input {
-            height: 42px;
-            padding-left: 40px;
-            font-size: 16px;
-            border: 1px solid #c8cccf;
-            color: inherit;
-        }
-        .login-submit {
-            margin-top: 30px;
-        }
-        .layadmin-user-login-icon {
+
+        .login-shell::before {
+            content: "";
             position: absolute;
-            left: 1px;
-            top: 1px;
-            width: 38px;
-            line-height: 40px;
-            text-align: center;
-            color: #B2B2B2;
-            font-size: 18px;
+            inset: 0;
+            background:
+                linear-gradient(120deg, rgba(6, 18, 38, 0.40), transparent 42%),
+                linear-gradient(300deg, rgba(16, 66, 98, 0.30), transparent 38%);
+            pointer-events: none;
         }
-        .layui-btn-normal {
-            background-color: rgba(64,158,255,0.9);
-            font-size: 20px;
-            border-radius: 4px;
-            height: 52px;
+
+        .login-layout {
+            width: min(1180px, 100%);
+            display: grid;
+            grid-template-columns: minmax(0, 1.1fr) minmax(360px, 430px);
+            gap: 28px;
+            align-items: stretch;
+            position: relative;
+            z-index: 1;
         }
-        .login-lang {
+
+        .lang-switch {
             position: fixed;
-            top: 20px;
+            top: 18px;
             right: 24px;
-            z-index: 2;
+            z-index: 3;
         }
-        .login-lang select {
-            min-width: 140px;
+
+        .lang-switch select {
+            min-width: 144px;
             height: 34px;
-            padding: 0 10px;
-            border: 1px solid #d6dbe6;
-            border-radius: 17px;
-            background: rgba(255, 255, 255, 0.92);
+            padding: 0 12px;
+            border: 1px solid rgba(214, 219, 230, 0.94);
+            border-radius: 18px;
+            background: rgba(255, 255, 255, 0.94);
             color: #3b4a5a;
             outline: none;
         }
+
+        .hero-panel {
+            min-height: 620px;
+            border-radius: 30px;
+            padding: 42px 40px 36px;
+            box-sizing: border-box;
+            color: #eef5ff;
+            position: relative;
+            overflow: hidden;
+            background:
+                radial-gradient(520px 280px at 18% 12%, rgba(116, 193, 255, 0.30), transparent 64%),
+                radial-gradient(480px 260px at 92% 88%, rgba(58, 214, 182, 0.26), transparent 60%),
+                linear-gradient(155deg, rgba(13, 39, 73, 0.88), rgba(12, 76, 104, 0.62));
+            border: 1px solid rgba(200, 225, 255, 0.18);
+            box-shadow: 0 32px 70px rgba(6, 17, 36, 0.32);
+        }
+
+        .hero-panel::before,
+        .hero-panel::after {
+            content: "";
+            position: absolute;
+            border-radius: 999px;
+            background: rgba(255, 255, 255, 0.07);
+        }
+
+        .hero-panel::before {
+            width: 420px;
+            height: 420px;
+            top: -220px;
+            right: -140px;
+        }
+
+        .hero-panel::after {
+            width: 260px;
+            height: 260px;
+            left: -80px;
+            bottom: -120px;
+        }
+
+        .brand-chip {
+            display: inline-flex;
+            align-items: center;
+            gap: 10px;
+            padding: 8px 14px;
+            border-radius: 999px;
+            background: rgba(255, 255, 255, 0.10);
+            border: 1px solid rgba(255, 255, 255, 0.12);
+            font-size: 12px;
+            letter-spacing: 0.12em;
+            text-transform: uppercase;
+        }
+
+        .brand-chip::before {
+            content: "";
+            width: 8px;
+            height: 8px;
+            border-radius: 50%;
+            background: #7ff6d6;
+            box-shadow: 0 0 0 6px rgba(127, 246, 214, 0.12);
+        }
+
+        .hero-title {
+            margin: 24px 0 14px;
+            font-size: 44px;
+            line-height: 1.08;
+            font-weight: 700;
+            letter-spacing: -0.02em;
+        }
+
+        .hero-subtitle {
+            max-width: 560px;
+            color: rgba(236, 245, 255, 0.82);
+            line-height: 1.8;
+            font-size: 15px;
+        }
+
+        .hero-metrics {
+            display: grid;
+            grid-template-columns: repeat(3, minmax(0, 1fr));
+            gap: 14px;
+            margin-top: 34px;
+        }
+
+        .metric-card {
+            padding: 18px 18px 16px;
+            border-radius: 20px;
+            background: rgba(255, 255, 255, 0.08);
+            border: 1px solid rgba(255, 255, 255, 0.10);
+            backdrop-filter: blur(8px);
+        }
+
+        .metric-value {
+            font-size: 28px;
+            font-weight: 700;
+            line-height: 1.1;
+            margin-bottom: 8px;
+        }
+
+        .metric-label {
+            color: rgba(236, 245, 255, 0.70);
+            font-size: 13px;
+        }
+
+        .hero-footer {
+            position: absolute;
+            left: 40px;
+            right: 40px;
+            bottom: 30px;
+            display: flex;
+            justify-content: space-between;
+            gap: 16px;
+            font-size: 12px;
+            color: rgba(236, 245, 255, 0.60);
+        }
+
+        .login-card {
+            width: 100%;
+            min-height: 620px;
+            border-radius: 30px;
+            background: rgba(255, 255, 255, 0.92);
+            border: 1px solid rgba(219, 227, 238, 0.96);
+            box-shadow: 0 28px 60px rgba(8, 28, 61, 0.18);
+            overflow: hidden;
+            backdrop-filter: blur(16px);
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+        }
+
+        .login-head {
+            padding: 36px 38px 20px;
+        }
+
+        .login-title {
+            margin: 0;
+            font-size: 32px;
+            font-weight: 700;
+            color: #17324f;
+            cursor: pointer;
+            user-select: none;
+        }
+
+        .login-subtitle {
+            margin-top: 10px;
+            color: #6d7f90;
+            font-size: 14px;
+            line-height: 1.6;
+        }
+
+        .login-body {
+            padding: 0 38px 38px;
+        }
+
+        .login-form .el-form-item {
+            margin-bottom: 20px;
+        }
+
+        .login-form .el-input__inner,
+        .login-form .el-button {
+            height: 48px;
+            line-height: 48px;
+        }
+
+        .login-form .el-input__prefix {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #91a0b2;
+        }
+
+        .login-form .el-input__inner {
+            border-radius: 16px;
+            border-color: rgba(214, 223, 235, 0.94);
+            background: rgba(248, 251, 255, 0.84);
+            padding-left: 44px;
+        }
+
+        .login-form .el-input__inner:focus {
+            background: #fff;
+            border-color: #4a8df0;
+        }
+
+        .login-submit {
+            width: 100%;
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+            font-size: 16px;
+            font-weight: 600;
+            border-radius: 16px;
+            margin-top: 6px;
+            box-shadow: 0 14px 24px rgba(46, 115, 223, 0.28);
+        }
+
+        .tools-dialog .el-dialog,
+        .text-dialog .el-dialog,
+        .upload-dialog .el-dialog {
+            border-radius: 20px;
+            overflow: hidden;
+        }
+
+        .tools-dialog .el-dialog__header,
+        .text-dialog .el-dialog__header,
+        .upload-dialog .el-dialog__header {
+            padding: 18px 20px 12px;
+            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
+            background: #f8fbff;
+        }
+
+        .tools-dialog .el-dialog__title,
+        .text-dialog .el-dialog__title,
+        .upload-dialog .el-dialog__title {
+            font-weight: 700;
+            color: #243447;
+        }
+
+        .tools-dialog .el-dialog__body,
+        .text-dialog .el-dialog__body,
+        .upload-dialog .el-dialog__body {
+            padding: 18px 20px 20px;
+        }
+
+        .tool-group + .tool-group {
+            margin-top: 18px;
+            padding-top: 18px;
+            border-top: 1px solid rgba(235, 240, 246, 0.95);
+        }
+
+        .tool-title {
+            font-weight: 700;
+            color: #43576b;
+            margin-bottom: 10px;
+        }
+
+        .tool-desc {
+            margin-top: 8px;
+            font-size: 12px;
+            color: #8c9aac;
+            line-height: 1.6;
+        }
+
+        .tool-actions {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 10px;
+        }
+
+        .tool-actions .el-button {
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+        }
+
+        .dialog-text-label {
+            font-weight: 700;
+            color: #43576b;
+            margin-bottom: 8px;
+        }
+
+        .dialog-text-tip {
+            margin-bottom: 10px;
+            color: #8c9aac;
+            font-size: 12px;
+            line-height: 1.6;
+        }
+
+        .text-footer,
+        .upload-footer {
+            display: flex;
+            justify-content: flex-end;
+            gap: 10px;
+            margin-top: 14px;
+        }
+
+        .text-footer .el-button,
+        .upload-footer .el-button {
+            min-width: 96px;
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+        }
+
+        @media (max-width: 640px) {
+            .login-shell {
+                padding-top: 72px;
+            }
+
+            .lang-switch {
+                right: 12px;
+                top: 12px;
+            }
+
+            .login-head,
+            .login-body {
+                padding-left: 20px;
+                padding-right: 20px;
+            }
+
+            .login-title {
+                font-size: 26px;
+            }
+        }
+
+        @media (max-width: 980px) {
+            .login-layout {
+                grid-template-columns: 1fr;
+                max-width: 430px;
+            }
+
+            .hero-panel {
+                min-height: auto;
+                padding: 28px 24px 78px;
+            }
+
+            .hero-title {
+                font-size: 32px;
+            }
+
+            .hero-metrics {
+                grid-template-columns: 1fr;
+            }
+
+            .hero-footer {
+                left: 24px;
+                right: 24px;
+                bottom: 22px;
+                flex-direction: column;
+            }
+
+            .login-card {
+                min-height: auto;
+            }
+        }
     </style>
 </head>
-<body class="login-bg animsition">
+<body>
+<div id="app" class="login-shell" v-cloak>
+    <div class="lang-switch">
+        <select v-model="currentLocale" aria-label="Language" @change="handleLocaleChange">
+            <option v-for="item in localeOptions" :key="item.tag" :value="item.tag">{{ item.label }}</option>
+        </select>
+    </div>
 
-<div class="login-lang">
-    <select id="lang-switch" aria-label="Language"></select>
-</div>
-<div id="login-wrapper" class="animate__animated animate__bounceInDown">
-    <header>
-        <h2 id="login-title" data-i18n-key="login.title" style="cursor: pointer; user-select: none;">WCS绯荤粺V3.0</h2>
-    </header>
-    <div class="layui-form layadmin-user-login-body">
-        <div class="layui-form-item">
-            <label class="layui-icon layui-icon-cellphone layadmin-user-login-icon"></label>
-            <input id="mobile" class="layui-input" type="text" name="mobile" lay-verify="mobile" data-i18n-placeholder-key="login.username" placeholder="璐﹀彿">
-        </div>
-        <div class="layui-form-item">
-            <label class="layui-icon layui-icon-password layadmin-user-login-icon"></label>
-            <input id="password" class="layui-input" type="password" name="password" lay-verify="password" data-i18n-placeholder-key="login.password" placeholder="瀵嗙爜">
-        </div>
-    </div>
-    <div class="layui-form-item login-submit">
-        <button id="login-button" data-i18n-key="login.submit" class="layui-btn layui-btn-fluid layui-btn-normal" lay-submit="" lay-filter="login">鐧� &nbsp  &nbsp 褰�</button>
-    </div>
-</div>
-<div id="system-tools-panel" style="display: none; padding: 20px;">
-    <div style="margin-bottom: 18px;">
-        <div data-i18n-key="login.tools.recommended" style="margin-bottom: 10px; color: #666; font-weight: 600;">鎺ㄨ崘鎿嶄綔</div>
-        <div style="display: flex; flex-wrap: wrap; gap: 12px;">
-            <button data-i18n-key="login.tools.requestCode" class="layui-btn layui-btn-normal layui-btn-sm" id="btn-request-code">鑾峰彇璇锋眰鐮�</button>
-            <button data-i18n-key="login.tools.activate" class="layui-btn layui-btn-normal layui-btn-sm" id="btn-activate">涓�閿縺娲�</button>
-        </div>
-        <div data-i18n-key="login.tools.recommendedDesc" style="margin-top: 8px; color: #999; font-size: 12px;">浼樺厛浣跨敤鈥滆幏鍙栬姹傜爜鈥濆拰鈥滀竴閿縺娲烩�濆畬鎴愯鍙瘉鐢宠涓庢縺娲汇��</div>
-    </div>
-    <div>
-        <div data-i18n-key="login.tools.others" style="margin-bottom: 10px; color: #666; font-weight: 600;">鍏朵粬宸ュ叿</div>
-        <div style="display: flex; flex-wrap: wrap; gap: 12px;">
-            <button data-i18n-key="login.tools.projectName" class="layui-btn layui-btn-primary layui-btn-sm" id="btn-project-name">鑾峰彇椤圭洰鍚嶇О</button>
-            <button data-i18n-key="login.tools.serverInfo" class="layui-btn layui-btn-primary layui-btn-sm" id="btn-server-info">鑾峰彇绯荤粺閰嶇疆</button>
-            <button data-i18n-key="login.tools.uploadLicense" class="layui-btn layui-btn-primary layui-btn-sm" id="btn-upload-license">褰曞叆璁稿彲璇�</button>
-        </div>
-    </div>
-</div>
+    <div class="login-layout">
+        <section class="hero-panel animate__animated animate__fadeInLeft">
+            <div class="brand-chip">Zoneyung WCS</div>
+            <div class="hero-title">WCS绯荤粺璁╄澶囪皟搴︺�佷换鍔℃墽琛屼笌鐜板満鐩戞帶淇濇寔鍦ㄥ悓涓�濂椾笟鍔¢摼璺腑銆�</div>
+            <div class="hero-subtitle">
+                Warehouse Control System 闈㈠悜鑷姩鍖栫珛浣撲粨搴撶幇鍦烘墽琛屽眰锛屽洿缁曞爢鍨涙満銆丷GV銆佽緭閫佺珯鍙般�佷换鍔℃寚浠ゃ��
+                搴撲綅鐘舵�佸拰鏃ュ織杩借釜杩涜缁熶竴璋冨害涓庡彲瑙嗗寲绠$悊锛屽府鍔╀粨鍌ㄧ郴缁熷疄鐜扮ǔ瀹氥�佸彲杩芥函銆佸彲鑱斿姩鐨勪綔涓氭帶鍒躲��
+                娴欐睙涓壃绔嬪簱鎶�鏈湁闄愬叕鍙搁暱鏈熶笓娉ㄤ簬鑷姩鍖栫珛浣撲粨搴撲笌鏅鸿兘鐗╂祦绯荤粺寤鸿锛岃鐩栨柟妗堣璁°�佽蒋浠舵帶鍒躲��
+                璁惧闆嗘垚涓庨」鐩疄鏂戒氦浠樸��
+            </div>
+            <div class="hero-metrics">
+                <div class="metric-card">
+                    <div class="metric-value">璋冨害</div>
+                    <div class="metric-label">缁熶竴缂栨帓鐜板満鎵ц浠诲姟</div>
+                </div>
+                <div class="metric-card">
+                    <div class="metric-value">杩芥函</div>
+                    <div class="metric-label">浣滀笟銆佽澶囥�佹棩蹇楀叏閾捐矾鐣欑棔</div>
+                </div>
+                <div class="metric-card">
+                    <div class="metric-value">闆嗘垚</div>
+                    <div class="metric-label">瀵规帴WMS銆佽澶囦笌涓氬姟瑙勫垯</div>
+                </div>
+            </div>
+            <div class="hero-footer">
+                <span>娴欐睙涓壃绔嬪簱鎶�鏈湁闄愬叕鍙�</span>
+                <span>鑷姩鍖栫珛浣撲粨搴撲笌鏅鸿兘鐗╂祦绯荤粺瑙e喅鏂规</span>
+            </div>
+        </section>
 
+        <section class="login-card animate__animated animate__fadeInUp">
+            <div class="login-head">
+                <h1 class="login-title" @click="handleTitleClick">{{ text('login.title', 'WCS绯荤粺V3.0') }}</h1>
+                <div class="login-subtitle">璇疯緭鍏ヨ处鍙峰拰瀵嗙爜杩涘叆绯荤粺銆�</div>
+            </div>
+            <div class="login-body">
+                <el-form ref="loginForm" class="login-form" :model="loginForm" :rules="loginRules" @submit.native.prevent>
+                    <el-form-item prop="mobile">
+                        <el-input v-model.trim="loginForm.mobile" :placeholder="text('login.username', '璐﹀彿')" clearable @keyup.enter.native="handleLogin">
+                            <i slot="prefix" class="el-input__icon el-icon-user"></i>
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item prop="password">
+                        <el-input v-model="loginForm.password" type="password" show-password :placeholder="text('login.password', '瀵嗙爜')" @keyup.enter.native="handleLogin">
+                            <i slot="prefix" class="el-input__icon el-icon-lock"></i>
+                        </el-input>
+                    </el-form-item>
+                    <el-button class="login-submit" type="primary" :loading="loginLoading" @click="handleLogin">
+                        {{ text('login.submit', '鐧诲綍') }}
+                    </el-button>
+                </el-form>
+            </div>
+        </section>
+    </div>
+
+    <el-dialog class="tools-dialog" title="绯荤粺宸ュ叿" :visible.sync="toolsDialogVisible" width="560px" :close-on-click-modal="true" append-to-body>
+        <div class="tool-group">
+            <div class="tool-title">{{ text('login.tools.recommended', '鎺ㄨ崘鎿嶄綔') }}</div>
+            <div class="tool-actions">
+                <el-button type="primary" size="small" @click="requestCode">{{ text('login.tools.requestCode', '鑾峰彇璇锋眰鐮�') }}</el-button>
+                <el-button type="primary" size="small" plain @click="activateLicense">{{ text('login.tools.activate', '涓�閿縺娲�') }}</el-button>
+            </div>
+            <div class="tool-desc">{{ text('login.tools.recommendedDesc', '浼樺厛浣跨敤鈥滆幏鍙栬姹傜爜鈥濆拰鈥滀竴閿縺娲烩�濆畬鎴愯鍙瘉鐢宠涓庢縺娲汇��') }}</div>
+        </div>
+        <div class="tool-group">
+            <div class="tool-title">{{ text('login.tools.others', '鍏朵粬宸ュ叿') }}</div>
+            <div class="tool-actions">
+                <el-button size="small" @click="getProjectName">{{ text('login.tools.projectName', '鑾峰彇椤圭洰鍚嶇О') }}</el-button>
+                <el-button size="small" @click="getServerInfo">{{ text('login.tools.serverInfo', '鑾峰彇绯荤粺閰嶇疆') }}</el-button>
+                <el-button size="small" @click="uploadDialogVisible = true">{{ text('login.tools.uploadLicense', '褰曞叆璁稿彲璇�') }}</el-button>
+            </div>
+        </div>
+    </el-dialog>
+
+    <el-dialog class="text-dialog" :title="textDialog.title" :visible.sync="textDialogVisible" width="720px" append-to-body>
+        <div class="dialog-text-label">{{ textDialog.label }}</div>
+        <div v-if="textDialog.tip" class="dialog-text-tip">{{ textDialog.tip }}</div>
+        <el-input v-model="textDialog.text" type="textarea" :rows="10" readonly></el-input>
+        <div class="text-footer">
+            <el-button @click="textDialogVisible = false">鍏抽棴</el-button>
+            <el-button type="primary" @click="copyText">{{ text('copy', '澶嶅埗') }}</el-button>
+        </div>
+    </el-dialog>
+
+    <el-dialog class="upload-dialog" title="褰曞叆璁稿彲璇�" :visible.sync="uploadDialogVisible" width="760px" append-to-body>
+        <div class="dialog-text-label">璁稿彲璇� Base64</div>
+        <div class="dialog-text-tip">灏嗚鍙瘉鏈嶅姟绔繑鍥炵殑 license 瀛楁瀹屾暣绮樿创鍒拌繖閲屻��</div>
+        <el-input v-model.trim="licenseBase64" type="textarea" :rows="10"></el-input>
+        <div class="upload-footer">
+            <el-button @click="uploadDialogVisible = false">鍙栨秷</el-button>
+            <el-button type="primary" @click="submitLicense">鎻愪氦</el-button>
+        </div>
+    </el-dialog>
+</div>
 </body>
 <script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
 <script type="text/javascript" src="../static/js/tools/md5.js"></script>
-<script type="text/javascript">
-
-    layui.use(['form','layer'],function () {
-        var form = layui.form,
-            layer = layui.layer,
-            $ = layui.jquery;
-
-        function initLanguageSwitch() {
-            WCS_I18N.onReady(function (i18n) {
-                var select = document.getElementById('lang-switch');
-                var options = i18n.getLocaleOptions();
-                var current = i18n.getLocale();
-                var html = '';
-                var i;
-                for (i = 0; i < options.length; i++) {
-                    html += '<option value="' + options[i].tag + '"' + (options[i].tag === current ? ' selected' : '') + '>' + options[i].label + '</option>';
-                }
-                select.innerHTML = html;
-                select.onchange = function () {
-                    i18n.setLocale(this.value);
-                };
-                document.title = i18n.t('login.title');
-            });
-        }
-
-        initLanguageSwitch();
-
-        // 杩炵画鐐瑰嚮涓夋鏍囬鏄剧ず闅愯棌鍔熻兘
-        var titleClickCount = 0;
-        var titleClickTimer = null;
-        $('#login-title').click(function() {
-            titleClickCount++;
-            if (titleClickTimer) {
-                clearTimeout(titleClickTimer);
-            }
-            if (titleClickCount >= 3) {
-                titleClickCount = 0;
-                openSystemToolsDialog();
-            } else {
-                titleClickTimer = setTimeout(function() {
-                    titleClickCount = 0;
-                }, 500);
-            }
-        });
-
-        function openSystemToolsDialog() {
-            layer.open({
-                type: 1,
-                title: '绯荤粺宸ュ叿',
-                area: ['560px', '300px'],
-                shadeClose: true,
-                content: $('#system-tools-panel'),
-                end: function () {
-                    $('#system-tools-panel').hide();
-                }
-            });
-        }
-
-        function fallbackCopy(text) {
-            try {
-                var textarea = document.createElement('textarea');
-                textarea.value = text;
-                textarea.style.position = 'fixed';
-                textarea.style.opacity = '0';
-                document.body.appendChild(textarea);
-                textarea.select();
-                document.execCommand('copy');
-                document.body.removeChild(textarea);
-                layer.msg('宸插鍒跺埌鍓创鏉�');
-            } catch (err) {
-                layer.msg('澶嶅埗澶辫触');
-            }
-        }
-
-        function openTextDialog(title, label, text, tip) {
-            var html = ''
-                + '<div style="padding:15px 20px 5px 20px;">'
-                +   '<div style="font-weight:600;margin-bottom:8px;">' + label + '</div>'
-                +   (tip ? '<div style="color:#999;margin-bottom:8px;">' + tip + '</div>' : '')
-                +   '<textarea id="dialog-text" readonly style="width:100%;min-height:220px;background:#f7f7f7;border:1px solid #e6e6e6;border-radius:6px;padding:12px;line-height:1.6;resize:none;">'
-                +       text
-                +   '</textarea>'
-                +   '<div class="layui-btn-container" style="text-align:right;margin-top:10px;">'
-                +       '<button class="layui-btn layui-btn-primary" id="copy-dialog-text">澶嶅埗</button>'
-                +   '</div>'
-                + '</div>';
-            layer.open({
-                type: 1,
-                title: title,
-                area: ['720px','460px'],
-                shadeClose: true,
-                content: html,
-                success: function (layero) {
-                    layero.find('#copy-dialog-text').on('click', function () {
-                        var value = layero.find('#dialog-text').val();
-                        if (navigator.clipboard && navigator.clipboard.writeText) {
-                            navigator.clipboard.writeText(value).then(function () {
-                                layer.msg('宸插鍒跺埌鍓创鏉�');
-                            }).catch(function () {
-                                fallbackCopy(value);
-                            });
-                        } else {
-                            fallbackCopy(value);
-                        }
-                    });
-                }
-            });
-        }
-
-        // 鑾峰彇璇锋眰鐮�
-        $('#btn-request-code').click(function() {
-            $.ajax({
-                url: baseUrl + "/license/getRequestCode",
-                method: 'GET',
-                success: function (res) {
-                    if (res.code === 200){
-                        openTextDialog('鑾峰彇璇锋眰鐮�', '璇锋眰鐮�', res.msg || '', '璇锋眰鐮佷腑宸插寘鍚」鐩悕绉帮紝鐩存帴鍙戠粰璁稿彲璇佹湇鍔$鍗冲彲銆�');
-                    } else {
-                        layer.msg(res.msg || '鑾峰彇璇锋眰鐮佸け璐�');
-                    }
-                },
-                error: function () {
-                    layer.msg('鑾峰彇璇锋眰鐮佸け璐�');
-                }
-            });
-            return false;
-        });
-
-        // 鑾峰彇绯荤粺閰嶇疆
-        $('#btn-server-info').click(function() {
-            $.ajax({
-                url: baseUrl + "/license/getServerInfos",
-                method: 'GET',
-                success: function (res) {
-                    var pretty = '';
-                    try {
-                        pretty = JSON.stringify(res, null, 2);
-                    } catch (e) {
-                        pretty = res;
-                    }
-                    openTextDialog('鑾峰彇绯荤粺閰嶇疆', '绯荤粺閰嶇疆淇℃伅', pretty, '鑰侀」鐩粛鍙户缁娇鐢ㄨ繖浠界‖浠朵俊鎭� JSON 鐢宠璁稿彲璇併��');
-                },
-                error: function () {
-                    layer.msg('鑾峰彇绯荤粺閰嶇疆淇℃伅澶辫触');
-                }
-            });
-            return false;
-        });
-
-        // 褰曞叆璁稿彲璇�
-        $('#btn-upload-license').click(function () {
-            layer.open({
-                type: 1,
-                title: '褰曞叆璁稿彲璇�',
-                area: ['760px', '420px'],
-                shadeClose: true,
-                content: ''
-                    + '<div style="padding:15px 20px 5px 20px;">'
-                    +   '<div style="font-weight:600;margin-bottom:8px;">璁稿彲璇� Base64</div>'
-                    +   '<div style="color:#999;margin-bottom:8px;">灏嗚鍙瘉鏈嶅姟绔繑鍥炵殑 license 瀛楁瀹屾暣绮樿创鍒拌繖閲屻��</div>'
-                    +   '<textarea id="license-base64-input" style="width:100%;min-height:240px;background:#fff;border:1px solid #e6e6e6;border-radius:6px;padding:12px;line-height:1.6;resize:none;"></textarea>'
-                    + '</div>',
-                btn: ['鎻愪氦', '鍙栨秷'],
-                yes: function (index, layero) {
-                    var license = $.trim(layero.find('#license-base64-input').val());
-                    if (!license) {
-                        layer.msg('璁稿彲璇佸唴瀹逛笉鑳戒负绌�');
-                        return;
-                    }
-                    $.ajax({
-                        url: baseUrl + '/license/updateLicense',
-                        method: 'POST',
-                        contentType: 'application/json;charset=UTF-8',
-                        data: JSON.stringify({license: license}),
-                        success: function (res) {
-                            if (res.code === 200) {
-                                layer.close(index);
-                                layer.msg('璁稿彲璇佹洿鏂版垚鍔�');
-                            } else {
-                                layer.msg(res.msg || '璁稿彲璇佹洿鏂板け璐�');
-                            }
-                        },
-                        error: function () {
-                            layer.msg('璁稿彲璇佸綍鍏ュけ璐�');
-                        }
-                    });
-                }
-            });
-            return false;
-        });
-
-        // 涓�閿縺娲�
-        $('#btn-activate').click(function() {
-            layer.confirm('纭畾鎵ц涓�閿縺娲诲悧', function(index){
-                layer.close(index);
-                $.ajax({
-                    url: baseUrl + "/license/activate",
-                    method: 'POST',
-                    success: function (res) {
-                        if (res.code === 200){
-                            layer.msg('婵�娲绘垚鍔�');
-                        } else {
-                            layer.msg(res.msg)
-                        }
-                    },
-                    error: function () {
-                        layer.msg('婵�娲诲け璐�');
-                    }
-                });
-            });
-            return false;
-        });
-
-        // 鑾峰彇椤圭洰鍚嶇О
-        $('#btn-project-name').click(function() {
-            $.ajax({
-                url: baseUrl + "/license/getProjectName",
-                method: 'GET',
-                success: function (res) {
-                    if (res.code === 200){
-                        layer.alert(res.msg);
-                    } else {
-                        layer.msg(res.msg)
-                    }
-                },
-                error: function () {
-                    layer.msg('鑾峰彇椤圭洰鍚嶇О澶辫触');
-                }
-            });
-            return false;
-        });
-
-        form.on('submit(login)', function (data) {
-            var mobile = $("#mobile").val();
-            if (mobile === "") {
-                layer.msg("璇疯緭鍏ヨ处鍙�", {offset: '150px'});
-                return;
-            }
-            var password = $("#password").val();
-            if (password === "") {
-                layer.msg("璇疯緭鍏ュ瘑鐮�", {offset: '150px'});
-                return;
-            }
-
-            var user = {
-                mobile: mobile,
-                password: hex_md5(password)
-            };
-            $.ajax({
-                url: baseUrl+"/login.action",
-                data: user,
-                method: 'POST',
-                success: function (res) {
-                    if (res.code === 200){
-                        localStorage.setItem("token", res.data.token);
-                        localStorage.setItem("username", res.data.username);
-                        window.location.href = "index.html";
-                    } else {
-                        layer.msg(res.msg, {offset: '150px'});
-                    }
-                }
-            });
-            return false;
-        });
-
-        $('body').keydown(function () {
-            if (event.keyCode === 13) {
-                $("#login-button").click();
-            }
-        });
-
-    });
-</script>
-<script type></script>
+<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></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/login/login.js?v=20260310_login_vue"></script>
 </html>
diff --git a/src/main/webapp/views/operateLog/operateLog.html b/src/main/webapp/views/operateLog/operateLog.html
index 3fb63d9..61b1d37 100644
--- a/src/main/webapp/views/operateLog/operateLog.html
+++ b/src/main/webapp/views/operateLog/operateLog.html
@@ -1,70 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>OperateLog 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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>
 
-<!-- 鎼滅储鏍� -->
-<div id="search-box" class="layui-form layui-card-header">
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
-        </div>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline cool-auto-complete">
-            <input id="userId" class="layui-input" name="user_id" type="text" placeholder="璇疯緭鍏�" autocomplete="off" style="display: none">
-            <input id="userUsername" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="鐢ㄦ埛" onfocus=this.blur()>
-            <div class="cool-auto-complete-window">
-                <input class="cool-auto-complete-window-input" data-key="userQuery" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                <select class="cool-auto-complete-window-select" data-key="userQuerySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                </select>
+            <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>
-    </div>
-    <!-- 鏃ユ湡鑼冨洿 -->
-    <div class="layui-inline" style="width: 300px">
-        <div class="layui-input-inline">
-            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 OperateLog' : '淇敼 OperateLog'"
+        :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>
-    </div>
-    <!-- 寰呮坊鍔� -->
-    <div id="data-search-btn" class="layui-btn-container layui-form-item">
-        <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>
-        <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>
-    </div>
+    </el-dialog>
 </div>
 
-<!-- 琛ㄦ牸 -->
-<table class="layui-hide" id="operateLog" lay-filter="operateLog"></table>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-<!--        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>-->
-<!--        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>-->
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="margin-top: 10px">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">璇︽儏</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/operateLog/operateLog.js" charset="utf-8"></script>
-
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/operateLog/operateLog.js?v=20260310" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/operateLog/operateLog_detail.html b/src/main/webapp/views/operateLog/operateLog_detail.html
deleted file mode 100644
index 1334bd2..0000000
--- a/src/main/webapp/views/operateLog/operateLog_detail.html
+++ /dev/null
@@ -1,87 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>璁块棶鍦板潃锛�</label>
-            <div class="layui-input-inline">
-                <input id="action" class="layui-input" type="text" placeholder="璁块棶鍦板潃" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鐢ㄣ��銆�鎴凤細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="userId" class="layui-input" type="text" placeholder="鐢ㄦ埛" lay-verify="required"  style="display: none">
-                <input id="userUsername" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="鐢ㄦ埛" onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQuery" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQuerySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>瀹㈡埛绔疘P锛�</label>
-            <div class="layui-input-inline">
-                <input id="ip" class="layui-input" type="text" placeholder="瀹㈡埛绔疘P" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>璇锋眰鏁版嵁锛�</label>
-            <div class="layui-input-inline">
-                <input id="request" class="layui-input" type="text" placeholder="璇锋眰鏁版嵁" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鍝嶅簲鏁版嵁锛�</label>
-            <div class="layui-input-inline">
-                <input id="response" class="layui-input" type="text" placeholder="鍝嶅簲鏁版嵁" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" placeholder="娣诲姞鏃堕棿" lay-verify="required"  autocomplete="off">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇濆瓨</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/operateLog/operateLog.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/password.html b/src/main/webapp/views/password.html
index fc866c0..4963a89 100644
--- a/src/main/webapp/views/password.html
+++ b/src/main/webapp/views/password.html
@@ -1,146 +1,103 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="UTF-8">
-    <title></title>
+    <title>淇敼瀵嗙爜</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/layui/css/layui.css" media="all">
+    <link rel="stylesheet" href="../static/vue/element/element.css">
     <style>
-        #password-div {
-            padding: 20px 20px 20px 13px;
+        [v-cloak] {
+            display: none;
         }
-        .layui-form-item {
-            margin-bottom: 8px;
+
+        html,
+        body {
+            margin: 0;
+            min-height: 100%;
+            background: #f6f8fb;
+            font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
         }
-        .layui-form-item .layui-input-inline {
-            margin: 0 0 10px 100px
+
+        .password-shell {
+            padding: 18px 16px 12px;
+            box-sizing: border-box;
         }
-        .layui-form-label {
-            color: #999!important;;
-            padding: 9px 0;
+
+        .password-card {
+            border-radius: 18px;
+            background: rgba(255, 255, 255, 0.98);
         }
-        #password-btn {
-            padding-top: 5px;
-            margin-left: 40px;
+
+        .password-form .el-form-item {
+            margin-bottom: 16px;
+        }
+
+        .password-form .el-input__inner,
+        .password-form .el-button {
+            height: 34px;
+            line-height: 34px;
+        }
+
+        .password-form .el-form-item__label {
+            font-weight: 600;
+            color: #5c6f82;
+            white-space: nowrap;
+        }
+
+        .password-form .el-button {
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+            padding: 0 18px;
+            vertical-align: middle;
+        }
+
+        .footer-bar {
             display: flex;
             justify-content: center;
-            align-items: center;
+            gap: 12px;
+            padding-top: 4px;
         }
-        #password-btn.layui-btn-container .layui-btn{
-            margin-right: 30px;
+
+        .footer-bar .el-button {
+            min-width: 120px;
         }
     </style>
 </head>
-<body id="body">
-<div id="password-div">
-    <div class="layui-form" lay-filter="">
-        <div class="layui-form-item">
-            <label class="layui-form-label">褰撳墠瀵嗙爜</label>
-            <div class="layui-input-inline">
-                <input id="oldPassword" type="password" class="layui-input" lay-verify="checkPwd" lay-vertype="tips" autocomplete="off">
+<body>
+<div id="passwordApp" class="password-shell" v-cloak>
+    <div class="password-card">
+        <el-form
+            ref="passwordForm"
+            class="password-form"
+            :model="form"
+            :rules="rules"
+            label-width="112px"
+            size="small"
+            @submit.native.prevent>
+            <el-form-item label="褰撳墠瀵嗙爜" prop="oldPassword">
+                <el-input v-model="form.oldPassword" type="password" show-password autocomplete="off"></el-input>
+            </el-form-item>
+            <el-form-item label="鏂板瘑鐮�" prop="password">
+                <el-input v-model="form.password" type="password" show-password autocomplete="off"></el-input>
+            </el-form-item>
+            <el-form-item label="纭鏂板瘑鐮�" prop="rePassword">
+                <el-input v-model="form.rePassword" type="password" show-password autocomplete="off"></el-input>
+            </el-form-item>
+            <div class="footer-bar">
+                <el-button @click="closeDialog">鍏抽棴</el-button>
+                <el-button type="primary" :loading="saving" @click="handleSave">淇濆瓨</el-button>
             </div>
-        </div>
-        <div class="layui-form-item">
-            <label class="layui-form-label">鏂板瘑鐮�</label>
-            <div class="layui-input-inline">
-                <input type="password"  class="layui-input" id="password" lay-verify="newPwd" lay-vertype="tips" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-form-item">
-            <label class="layui-form-label">纭鏂板瘑鐮�</label>
-            <div class="layui-input-inline">
-                <input type="password" class="layui-input" id="rePassword" lay-verify="rePwd" lay-vertype="tips" autocomplete="off">
-            </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div id="password-btn" class="layui-form-item layui-btn-container">
-            <div id="password-submit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="savePwd">淇濆瓨</div>
-            <div id="password-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
+        </el-form>
     </div>
 </div>
 </body>
 <script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
 <script type="text/javascript" src="../static/js/tools/md5.js"></script>
-<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script src="../static/layui/layui.js"></script>
-<script>
-    layui.use(['form'], function() {
-        var form = layui.form,
-            layer = layui.layer,
-            $ = layui.jquery;
-
-        form.verify({
-            checkPwd: function(val) {
-                if (val === ""){
-                    // return "褰撳墠瀵嗙爜涓嶈兘涓虹┖";
-                }
-                if (parent.$('#password').val() !== hex_md5(val)) {
-                    return "瀵嗙爜涓嶅尮閰�";
-                }
-            },
-            newPwd: function (val) {
-                if (val === ""){
-                    return "鏂板瘑鐮佷笉鑳戒负绌�";
-                }
-                if (val.length < 4) {
-                    return "涓嶈兘灏戜簬4涓瓧绗�";
-                }
-                if (parent.$('#password').val() === hex_md5(val)) {
-                    return "涓庢棫瀵嗙爜涓嶈兘鐩稿悓";
-                }
-            },
-            rePwd: function (val) {
-                if ($('#password').val() !== val){
-                    return "瀵嗙爜涓嶄竴鑷�";
-                }
-            }
-        });
-
-        form.on('submit(savePwd)', function (data) {
-            var user = {
-                id: parent.$('#id').val(),
-                password: hex_md5($('#password').val()),
-            };
-            $.ajax({
-                url: baseUrl+"/user/update/auth",
-                headers: {'token': localStorage.getItem('token')},
-                data: user,
-                method: 'POST',
-                success: function (res) {
-                    if (res.code === 200){
-                        parent.layer.close();
-                        layer.confirm('瀵嗙爜淇敼鎴愬姛锛岃閲嶆柊鐧诲綍',{
-                            btn: ['纭畾'],
-                            btn1: function(){
-                                localStorage.removeItem("token");
-                                top.location.href = baseUrl+"/";
-                            },
-                            cancel: function(){
-                                localStorage.removeItem("token");
-                                top.location.href = baseUrl+"/";
-                            },
-                            closeBtn: 0,
-                            shadeClose: false,
-                        })
-                    } else if (res.code === 403){
-                        top.location.href = baseUrl+"/";
-                    } else {
-                        layer.msg(res.msg);
-                    }
-                }
-            });
-            return false;
-        });
-
-
-    });
-
-    // 鍏抽棴鍔ㄤ綔
-    $(document).on('click','#password-close', function () {
-        parent.layer.closeAll();
-    });
-</script>
-</html>
\ No newline at end of file
+<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></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/password/password.js?v=20260310_password_vue"></script>
+</html>
diff --git a/src/main/webapp/views/permission/permission.html b/src/main/webapp/views/permission/permission.html
index b73d0e3..4fadf6f 100644
--- a/src/main/webapp/views/permission/permission.html
+++ b/src/main/webapp/views/permission/permission.html
@@ -1,66 +1,424 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>鏉冮檺绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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 {
+            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;
+        }
+
+        .toolbar-bar {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            gap: 8px;
+            flex-wrap: wrap;
+            padding: 12px 16px 10px;
+            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
+        }
+
+        .toolbar-left,
+        .toolbar-right {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .toolbar-title {
+            font-size: 13px;
+            color: #7a8a9f;
+        }
+
+        .toolbar-title strong {
+            color: var(--text-main);
+            font-size: 15px;
+            margin-right: 8px;
+        }
+
+        .toolbar-search {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .search-item.id {
+            width: 120px;
+        }
+
+        .search-item.resource {
+            width: 220px;
+        }
+
+        .toolbar-bar .el-input__inner,
+        .toolbar-bar .el-button {
+            height: 32px;
+            line-height: 32px;
+        }
+
+        .toolbar-bar .el-button {
+            padding: 0 12px;
+            border-radius: 8px;
+        }
+
+        .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;
+        }
+
+        .action-link {
+            color: #2f6bcb;
+            cursor: pointer;
+        }
+
+        .pager-bar {
+            padding: 0 16px 16px;
+            display: flex;
+            justify-content: flex-end;
+        }
+
+        .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 10px;
+        }
+
+        .dialog-footer {
+            display: flex;
+            justify-content: flex-end;
+            gap: 10px;
+        }
+
+        .detail-block {
+            display: grid;
+            grid-template-columns: repeat(2, minmax(0, 1fr));
+            gap: 14px 18px;
+        }
+
+        .detail-item {
+            min-width: 0;
+        }
+
+        .detail-item.full {
+            grid-column: 1 / -1;
+        }
+
+        .detail-label {
+            font-size: 12px;
+            color: #7c8a9d;
+            margin-bottom: 6px;
+        }
+
+        .detail-value {
+            min-height: 32px;
+            padding: 8px 10px;
+            border: 1px solid rgba(220, 229, 238, 0.95);
+            border-radius: 10px;
+            background: #f9fbfd;
+            color: var(--text-main);
+            word-break: break-all;
+        }
+
+        @media (max-width: 900px) {
+            .page-shell {
+                padding: 10px;
+            }
+
+            .toolbar-bar,
+            .table-wrap,
+            .pager-bar {
+                padding-left: 12px;
+                padding-right: 12px;
+            }
+
+            .search-item.id,
+            .search-item.resource {
+                width: 100%;
+            }
+
+            .detail-block {
+                grid-template-columns: 1fr;
+            }
+        }
+    </style>
 </head>
 <body>
-
-<!-- 鎼滅储鏍� -->
-<div id="search-box" class="layui-form layui-card-header">
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="id" placeholder="缂栧彿" autocomplete="off">
-        </div>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline cool-auto-complete">
-            <input id="resourceId" class="layui-input" name="resource_id" type="text" placeholder="璇疯緭鍏�" autocomplete="off" style="display: none">
-            <input id="resourceName" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="鎵�灞炶彍鍗�" onfocus=this.blur()>
-            <div class="cool-auto-complete-window">
-                <input class="cool-auto-complete-window-input" data-key="resourceQuery" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                <select class="cool-auto-complete-window-select" data-key="resourceQuerySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                </select>
+<div id="app" class="page-shell" v-cloak>
+    <section class="card-shell">
+        <div class="toolbar-bar">
+            <div class="toolbar-left">
+                <div class="toolbar-title"><strong>鏉冮檺绠$悊</strong></div>
+                <div class="toolbar-search">
+                    <el-input
+                        v-model.trim="searchForm.id"
+                        class="search-item id"
+                        clearable
+                        placeholder="缂栧彿">
+                    </el-input>
+                    <el-select
+                        v-model="searchForm.resourceId"
+                        class="search-item resource"
+                        clearable
+                        filterable
+                        remote
+                        reserve-keyword
+                        placeholder="鎵�灞炶彍鍗�"
+                        :remote-method="searchResourceOptions"
+                        :loading="resourceSearchLoading">
+                        <el-option
+                            v-for="item in resourceSearchOptions"
+                            :key="'search-' + item.id"
+                            :label="item.value"
+                            :value="item.id">
+                        </el-option>
+                    </el-select>
+                    <el-button size="small" type="primary" icon="el-icon-search" @click="handleSearch">鎼滅储</el-button>
+                    <el-button size="small" plain icon="el-icon-refresh-left" @click="handleReset">閲嶇疆</el-button>
+                </div>
+            </div>
+            <div class="toolbar-right">
+                <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-button size="small" plain icon="el-icon-download" :loading="exportLoading" @click="exportRows">瀵煎嚭</el-button>
+                <el-button size="small" plain icon="el-icon-refresh" :loading="loading" @click="loadList">鍒锋柊</el-button>
             </div>
         </div>
-    </div>
 
-    <!-- 寰呮坊鍔� -->
-    <div id="data-search-btn" class="layui-btn-container layui-form-item">
-        <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>
-        <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>
-    </div>
+        <div class="table-wrap">
+            <div class="table-shell">
+                <el-table
+                    ref="dataTable"
+                    v-loading="loading"
+                    :data="tableData"
+                    border
+                    stripe
+                    :height="tableHeight"
+                    @selection-change="handleSelectionChange">
+                    <el-table-column type="selection" width="52" align="center"></el-table-column>
+                    <el-table-column prop="id" label="ID" width="90" align="center"></el-table-column>
+                    <el-table-column prop="name" label="鏉冮檺鍚嶇О" min-width="180" show-overflow-tooltip></el-table-column>
+                    <el-table-column prop="action" label="鎺ュ彛鍦板潃" min-width="240" show-overflow-tooltip></el-table-column>
+                    <el-table-column label="鎵�灞炶彍鍗�" min-width="180" show-overflow-tooltip>
+                        <template slot-scope="scope">
+                            <span
+                                v-if="scope.row.resourceId && scope.row.resourceName"
+                                class="action-link"
+                                @click="openResourceDetail(scope.row)">
+                                {{ scope.row.resourceName }}
+                            </span>
+                            <span v-else>--</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="鐘舵��" width="100" align="center">
+                        <template slot-scope="scope">
+                            {{ scope.row['status$'] || '--' }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="鎿嶄綔" width="150" fixed="right" align="center">
+                        <template slot-scope="scope">
+                            <el-button type="text" @click="openDetailDialog(scope.row)">璇︽儏</el-button>
+                            <el-button type="text" @click="openEditDialog(scope.row)">缂栬緫</el-button>
+                        </template>
+                    </el-table-column>
+                </el-table>
+            </div>
+        </div>
+
+        <div class="pager-bar">
+            <el-pagination
+                background
+                layout="total, sizes, prev, pager, next, jumper"
+                :current-page="pagination.curr"
+                :page-sizes="[16, 30, 50, 100, 200, 500]"
+                :page-size="pagination.limit"
+                :total="pagination.total"
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange">
+            </el-pagination>
+        </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="permissionDialog.mode === 'create' ? '鏂板鏉冮檺' : permissionDialog.readonly ? '鏉冮檺璇︽儏' : '缂栬緫鏉冮檺'"
+        :visible.sync="permissionDialog.visible"
+        width="720px"
+        :close-on-click-modal="false">
+        <el-form
+            ref="permissionForm"
+            :model="permissionForm"
+            :rules="permissionRules"
+            label-width="90px"
+            size="small">
+            <el-row :gutter="16">
+                <el-col :span="12">
+                    <el-form-item label="鏉冮檺鍚嶇О" prop="name">
+                        <el-input v-model.trim="permissionForm.name" :disabled="permissionDialog.readonly" placeholder="璇疯緭鍏ユ潈闄愬悕绉�"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="鎺ュ彛鍦板潃" prop="action">
+                        <el-input v-model.trim="permissionForm.action" :disabled="permissionDialog.readonly" placeholder="璇疯緭鍏ユ帴鍙e湴鍧�"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="鎵�灞炶彍鍗�" prop="resourceId">
+                        <el-select
+                            v-model="permissionForm.resourceId"
+                            clearable
+                            filterable
+                            remote
+                            reserve-keyword
+                            style="width: 100%;"
+                            placeholder="璇烽�夋嫨鎵�灞炶彍鍗�"
+                            :disabled="permissionDialog.readonly"
+                            :remote-method="searchDialogResourceOptions"
+                            :loading="dialogResourceLoading">
+                            <el-option
+                                v-for="item in dialogResourceOptions"
+                                :key="'dialog-' + item.id"
+                                :label="item.value"
+                                :value="item.id">
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="鐘舵��" prop="status">
+                        <el-select v-model="permissionForm.status" :disabled="permissionDialog.readonly" style="width: 100%;" placeholder="璇烽�夋嫨鐘舵��">
+                            <el-option label="姝e父" :value="1"></el-option>
+                            <el-option label="绂佺敤" :value="0"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button @click="permissionDialog.visible = false">{{ permissionDialog.readonly ? '鍏抽棴' : '鍙栨秷' }}</el-button>
+            <el-button v-if="!permissionDialog.readonly" type="primary" :loading="permissionDialog.submitting" @click="submitPermission">淇濆瓨</el-button>
+        </div>
+    </el-dialog>
+
+    <el-dialog
+        class="dialog-panel"
+        title="鎵�灞炶彍鍗曡鎯�"
+        :visible.sync="resourceDetail.visible"
+        width="640px"
+        :close-on-click-modal="false">
+        <div class="detail-block">
+            <div class="detail-item">
+                <div class="detail-label">缂栧彿</div>
+                <div class="detail-value">{{ resourceDetail.data.id || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">鑿滃崟缂栫爜</div>
+                <div class="detail-value">{{ resourceDetail.data.code || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">鑿滃崟鍚嶇О</div>
+                <div class="detail-value">{{ resourceDetail.data.name || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">鐖剁骇鑿滃崟</div>
+                <div class="detail-value">{{ resourceDetail.data.resourceName || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">鑿滃崟绛夌骇</div>
+                <div class="detail-value">{{ resourceDetail.data['level$'] || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">鎺掑簭</div>
+                <div class="detail-value">{{ resourceDetail.data.sort || '--' }}</div>
+            </div>
+            <div class="detail-item full">
+                <div class="detail-label">鐘舵��</div>
+                <div class="detail-value">{{ resourceDetail.data['status$'] || '--' }}</div>
+            </div>
+        </div>
+        <div slot="footer" class="dialog-footer">
+            <el-button @click="resourceDetail.visible = false">鍏抽棴</el-button>
+        </div>
+    </el-dialog>
 </div>
 
-<!-- 琛ㄦ牸 -->
-<table class="layui-hide" id="permission" lay-filter="permission"></table>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">璇︽儏</a>
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/permission/permission.js" charset="utf-8"></script>
-
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/permission/permission.js?v=20260310_permission_vue_custom" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/permission/permission_detail.html b/src/main/webapp/views/permission/permission_detail.html
deleted file mode 100644
index 8e69961..0000000
--- a/src/main/webapp/views/permission/permission_detail.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鏉冮檺鍚嶇О锛�</label>
-            <div class="layui-input-inline">
-                <input id="name" class="layui-input" type="text" placeholder="鏉冮檺鍚嶇О" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鎺ュ彛鍦板潃锛�</label>
-            <div class="layui-input-inline">
-                <input id="action" class="layui-input" type="text" placeholder="鎺ュ彛鍦板潃" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鎵�灞炶彍鍗曪細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="resourceId" class="layui-input" type="text" placeholder="鎵�灞炶彍鍗�" style="display: none">
-                <input id="resourceName" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="鎵�灞炶彍鍗�" onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="resourceQuery" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="resourceQuerySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鐘躲��銆�鎬侊細</label>
-            <div class="layui-input-inline">
-                <select id="status" lay-verify="required">
-                    <option value="" style="display: none"></option>
-                    <option value="1">姝e父</option>
-                    <option value="0">绂佺敤</option>
-                </select>
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇濆瓨</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/permission/permission.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/resource/resource.html b/src/main/webapp/views/resource/resource.html
index a762410..89ce72e 100644
--- a/src/main/webapp/views/resource/resource.html
+++ b/src/main/webapp/views/resource/resource.html
@@ -1,309 +1,310 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>鑿滃崟鍒楄〃</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <link rel="stylesheet" href="../../static/vue/element/element.css">
+    <link rel="stylesheet" href="../../static/css/cool.css">
     <style>
-        #detail {
-            padding: 25px 30px 0 0;
+        :root {
+            --card-bg: rgba(255, 255, 255, 0.94);
+            --card-border: rgba(216, 226, 238, 0.95);
+            --text-main: #243447;
         }
-        .ew-tree-table-box {
-            height: 100%;
+
+        [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 {
+            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;
+        }
+
+        .toolbar-bar {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            gap: 10px;
+            flex-wrap: wrap;
+            padding: 12px 16px 10px;
+            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
+        }
+
+        .toolbar-left,
+        .toolbar-right {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .toolbar-bar .el-button {
+            padding: 8px 12px;
+            border-radius: 8px;
+        }
+
+        .toolbar-title {
+            font-size: 13px;
+            color: #7a8a9f;
+        }
+
+        .toolbar-title strong {
+            color: var(--text-main);
+            font-size: 15px;
+            margin-right: 8px;
+        }
+
+        .table-wrap {
+            padding: 10px 16px 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;
+        }
+
+        .menu-name {
+            display: inline-flex;
+            align-items: center;
+            gap: 8px;
+        }
+
+        .status-tag,
+        .level-tag {
+            min-width: 54px;
+            text-align: center;
+        }
+
+        .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;
+        }
+
+        .empty-wrap {
+            padding: 54px 0 72px;
+        }
+
+        @media (max-width: 900px) {
+            .page-shell {
+                padding: 10px;
+            }
+
+            .toolbar-bar,
+            .table-wrap {
+                padding-left: 12px;
+                padding-right: 12px;
+            }
         }
     </style>
 </head>
 <body>
-
-
-<!-- 姝f枃寮�濮� -->
-<div class="layui-fluid">
-    <div class="layui-card">
-        <div class="layui-card-body">
-            <!-- 鏁版嵁琛ㄦ牸 -->
-            <table id="resource"></table>
+<div id="app" class="page-shell" v-cloak>
+    <section class="card-shell">
+        <div class="toolbar-bar">
+            <div class="toolbar-left">
+                <div class="toolbar-title"><strong>鑿滃崟鍒楄〃</strong></div>
+            </div>
+            <div class="toolbar-right">
+                <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-button size="small" plain :icon="expandAll ? 'el-icon-folder-opened' : 'el-icon-folder'" @click="toggleExpandAll">
+                    {{ expandAll ? '鏀惰捣鍏ㄩ儴' : '灞曞紑鍏ㄩ儴' }}
+                </el-button>
+                <el-button size="small" plain icon="el-icon-refresh" :loading="loading" @click="loadTree">鍒锋柊</el-button>
+            </div>
         </div>
-    </div>
+
+        <div class="table-wrap">
+            <div class="table-shell">
+                <el-table
+                    ref="treeTable"
+                    v-loading="loading"
+                    :key="'resource-tree-' + tableKey"
+                    :data="tableData"
+                    row-key="id"
+                    border
+                    stripe
+                    :default-expand-all="expandAll"
+                    :tree-props="{ children: 'children' }"
+                    :height="tableHeight"
+                    @selection-change="handleSelectionChange">
+                    <el-table-column type="selection" width="52" align="center"></el-table-column>
+                    <el-table-column label="鑿滃崟鍚嶇О" min-width="260">
+                        <template slot-scope="scope">
+                            <div class="menu-name">
+                                <span>{{ scope.row.name }}</span>
+                            </div>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="code" label="鑿滃崟缂栫爜" min-width="220" show-overflow-tooltip></el-table-column>
+                    <el-table-column label="涓婄骇鑿滃崟" min-width="180" show-overflow-tooltip>
+                        <template slot-scope="scope">
+                            {{ parentName(scope.row.resourceId) || '--' }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="绫诲瀷" width="120" align="center">
+                        <template slot-scope="scope">
+                            <el-tag class="level-tag" size="mini" :type="levelTagType(scope.row.level)">
+                                {{ levelText(scope.row.level) }}
+                            </el-tag>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="sort" label="鎺掑簭" width="90" align="center"></el-table-column>
+                    <el-table-column label="鐘舵��" width="100" align="center">
+                        <template slot-scope="scope">
+                            <el-tag class="status-tag" size="mini" :type="Number(scope.row.status) === 1 ? 'success' : 'info'">
+                                {{ Number(scope.row.status) === 1 ? '姝e父' : '绂佺敤' }}
+                            </el-tag>
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="鎿嶄綔" width="150" 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.id])">鍒犻櫎</el-button>
+                        </template>
+                    </el-table-column>
+                </el-table>
+                <div v-if="!loading && tableData.length === 0" class="empty-wrap">
+                    <el-empty description="鏆傛棤鑿滃崟鏁版嵁"></el-empty>
+                </div>
+            </div>
+        </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板鑿滃崟' : '淇敼鑿滃崟'"
+        :visible.sync="dialog.visible"
+        width="680px"
+        :close-on-click-modal="false">
+        <el-form
+            ref="dialogForm"
+            :model="dialogForm"
+            :rules="dialogRules"
+            label-width="100px"
+            size="small">
+            <el-row :gutter="16">
+                <el-col :span="12">
+                    <el-form-item label="涓婄骇鑿滃崟" prop="resourceId">
+                        <el-cascader
+                            v-model="dialogForm.resourceId"
+                            :options="parentOptions"
+                            :props="parentCascaderProps"
+                            clearable
+                            filterable
+                            style="width: 100%;"
+                            placeholder="璇烽�夋嫨涓婄骇鑿滃崟">
+                        </el-cascader>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="鑿滃崟缂栫爜" prop="code">
+                        <el-input v-model.trim="dialogForm.code" placeholder="璇疯緭鍏ヨ彍鍗曠紪鐮�"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="绫诲瀷" prop="level">
+                        <el-select v-model="dialogForm.level" placeholder="璇烽�夋嫨绫诲瀷" style="width: 100%;">
+                            <el-option label="涓�绾ц彍鍗�" :value="1"></el-option>
+                            <el-option label="浜岀骇鑿滃崟" :value="2"></el-option>
+                            <el-option label="鎸夐挳" :value="3"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="鑿滃崟鍚嶇О" prop="name">
+                        <el-input v-model.trim="dialogForm.name" placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="鎺掑簭" prop="sort">
+                        <el-input v-model.number="dialogForm.sort" placeholder="璇疯緭鍏ユ帓搴�"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="鐘舵��" prop="status">
+                        <el-select v-model="dialogForm.status" placeholder="璇烽�夋嫨鐘舵��" style="width: 100%;">
+                            <el-option label="姝e父" :value="1"></el-option>
+                            <el-option label="绂佺敤" :value="0"></el-option>
+                        </el-select>
+                    </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/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs btn-edit" lay-event="edit">淇敼</a>
-    <a class="layui-btn layui-btn-danger layui-btn-xs btn-del" lay-event="del">鍒犻櫎</a>
-</script>
-
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form" style="margin: 0">
-        <input name="id" type="hidden">
-        <input name="uuid" type="hidden">
-        <input name="level" type="hidden">
-        <div class="layui-row">
-
-            <div class="layui-col-md6">
-
-                <div class="layui-form-item">
-                    <label class="layui-form-label">涓婄骇鑿滃崟</label>
-                    <div class="layui-input-block">
-                        <div id="resourceParentSel" class="ew-xmselect-tree"></div>
-                    </div>
-                </div>
-
-                <div class="layui-form-item">
-                    <label class="layui-form-label layui-form-required">鑿滃崟缂栫爜</label>
-                    <div class="layui-input-block">
-                        <input name="code" placeholder="璇疯緭鍏ヨ彍鍗曠紪鐮�" class="layui-input" lay-vertype="tips" lay-verify="required" required="">
-                    </div>
-                </div>
-
-                <div class="layui-form-item">
-                    <label class="layui-form-label layui-form-required">绫诲瀷</label>
-                    <div class="layui-input-block">
-                        <select name="level" lay-vertype="tips" lay-verify="required" required="">
-                            <option value="">璇烽�夋嫨绫诲瀷</option>
-                            <option value="1">涓�绾ц彍鍗�</option>
-                            <option value="2">浜岀骇鑿滃崟</option>
-                            <option value="3">鎸夐挳</option>
-                        </select>
-                    </div>
-                </div>
-
-            </div>
-
-            <div class="layui-col-md6">
-
-                <div class="layui-form-item">
-                    <label class="layui-form-label layui-form-required">鑿滃崟鍚嶇О</label>
-                    <div class="layui-input-block">
-                        <input name="name" placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�" class="layui-input" lay-vertype="tips" lay-verify="required" required="">
-                    </div>
-                </div>
-
-                <div class="layui-form-item">
-                    <label class="layui-form-label">鎺掑簭</label>
-                    <div class="layui-input-block">
-                        <input name="sort" placeholder="璇疯緭鍏ユ帓搴�" class="layui-input" lay-verify="number">
-                    </div>
-                </div>
-
-                <div class="layui-form-item">
-                    <label class="layui-form-label layui-form-required">鐘舵��</label>
-                    <div class="layui-input-block">
-                        <select name="status" lay-vertype="tips" lay-verify="required" required="">
-                            <option value="">璇烽�夋嫨鐘舵��</option>
-                            <option value="1">姝e父</option>
-                            <option value="0">绂佺敤</option>
-                        </select>
-                    </div>
-                </div>
-
-            </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
-<script type="text/html" id="typeTpl">
-    {{# if( d.level === 1 ){ }}
-    <span name="level" class="layui-badge layui-badge-green">鑿滃崟</span>
-    {{# } else if(d.level === 2){ }}
-    <span name="level" class="layui-badge layui-badge-green">鑿滃崟</span>
-    {{# } else if(d.level === 3){ }}
-    <span name="level" class="layui-badge layui-badge-gray">鎸夐挳</span>
-    {{# } }}
-</script>
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-
-<script>
-    layui.config({
-        base: baseUrl + "/static/layui/lay/modules/"
-    }).use(['form','treeTable', 'admin', 'xmSelect'], function() {
-        var $ = layui.jquery;
-        var layer = layui.layer;
-        var form = layui.form;
-        var admin = layui.admin;
-        var treeTable = layui.treeTable;
-        var xmSelect = layui.xmSelect;
-        var tbDataList = [];
-
-        var insTb = treeTable.render({
-            elem: '#resource',
-            url: baseUrl+'/resource/tree/auth',
-            headers: {token: localStorage.getItem('token')},
-            height: 'full-200',
-            toolbar: ['<p>',
-                '<button lay-event="add" class="layui-btn layui-btn-sm icon-btn"><i class="layui-icon">&#xe654;</i>娣诲姞</button>&nbsp;',
-                '<button lay-event="del" class="layui-btn layui-btn-sm layui-btn-danger icon-btn"><i class="layui-icon">&#xe640;</i>鍒犻櫎</button>',
-                '</p>'].join(''),
-            tree: {
-                iconIndex: 2,           // 鎶樺彔鍥炬爣鏄剧ず鍦ㄧ鍑犲垪
-                isPidData: true,        // 鏄惁鏄痠d銆乸id褰㈠紡鏁版嵁
-                idName: 'id',           // id瀛楁鍚嶇О
-                pidName: 'resourceId'     // pid瀛楁鍚嶇О
-            },
-            cols: [[
-                {type: 'checkbox', fixed: 'left'}
-                ,{field: 'id', title: 'ID', sort: true,align: 'center', fixed: 'left', width: 80, hide: true}
-                ,{field: 'name', align: 'left',title: '鑿滃崟鍚嶇О'}
-                ,{field: 'code', align: 'center',title: '鑿滃崟缂栫爜'}
-                // ,{field: 'resourceName', align: 'center',title: '鐖剁骇鑿滃崟'}
-                // ,{field: 'level$', align: 'center',title: '鑿滃崟绛夌骇'}
-                ,{field: 'type', align: 'center',title: '绫诲瀷', templet: '#typeTpl', width: 120}
-                ,{field: 'sort', align: 'center',title: '鎺掑簭'}
-
-                ,{fixed: 'right', title:'鎿嶄綔', align: 'center', toolbar: '#operate', width:150}
-            ]],
-            done: function (data) {
-                $('.ew-tree-table-box').css('height', '100%');
-                // insTb.expandAll();
-                tbDataList = data;
-                limit();
-            }
-        });
-
-        /* 琛ㄦ牸澶村伐鍏锋爮鐐瑰嚮浜嬩欢 */
-        treeTable.on('toolbar(resource)', function (obj) {
-            if (obj.event === 'add') { // 娣诲姞
-                showEditModel();
-            } else if (obj.event === 'del') { // 鍒犻櫎
-                var checkRows = insTb.checkStatus();
-                if (checkRows.length === 0) {
-                    layer.msg('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁', {icon: 2});
-                    return;
-                }
-                var ids = checkRows.map(function (d) {
-                    if (!d.LAY_INDETERMINATE) {
-                        return d.id;
-                    } else {
-                        return null;
-                    }
-                });
-                doDel({ids: ids});
-            }
-        });
-
-        /* 琛ㄦ牸鎿嶄綔鍒楃偣鍑讳簨浠� */
-        treeTable.on('tool(resource)', function (obj) {
-            if (obj.event === 'edit') { // 淇敼
-                showEditModel(obj.data);
-            } else if (obj.event === 'del') { // 鍒犻櫎
-                doDel(obj);
-            }
-        });
-
-        /* 鏄剧ず琛ㄥ崟寮圭獥 */
-        function showEditModel(mData) {
-            admin.open({
-                type: 1,
-                area: '600px',
-                title: (mData ? '淇敼' : '娣诲姞') + '鏉冮檺',
-                content: $('#editDialog').html(),
-                success: function (layero, dIndex) {
-                    // 鍥炴樉琛ㄥ崟鏁版嵁
-                    form.val('detail', mData);
-                    // 琛ㄥ崟鎻愪氦浜嬩欢
-                    form.on('submit(editSubmit)', function (data) {
-                        data.field.resourceId = insXmSel.getValue('valueStr');
-                        var loadIndex = layer.load(2);
-                        $.ajax({
-                            url: baseUrl+"/resource/"+(mData?'update':'add')+"/auth",
-                            headers: {'token': localStorage.getItem('token')},
-                            data: data.field,
-                            method: 'POST',
-                            success: function (res) {
-                                layer.close(loadIndex);
-                                if (res.code === 200){
-                                    layer.close(dIndex);
-                                    layer.msg(res.msg, {icon: 1});
-                                    insTb.refresh();
-                                    setTimeout(function () {
-                                        insTb.expand(data.field.resourceId);
-                                    }, 200)
-                                } else if (res.code === 403){
-                                    top.location.href = baseUrl+"/";
-                                }else {
-                                    layer.msg(res.msg, {icon: 2});
-                                }
-                            }
-                        })
-                        return false;
-                    });
-
-                    // 娓叉煋涓嬫媺鏍�
-                    var insXmSel = xmSelect.render({
-                        el: '#resourceParentSel',
-                        height: '250px',
-                        data: insTb.options.data,
-                        initValue: mData&&mData.resourceId!=null ? [mData.resourceId] : [],
-                        model: {label: {type: 'text'}},
-                        prop: {
-                            name: 'name',
-                            value: 'id'
-                        },
-                        radio: true,
-                        clickClose: true,
-                        tree: {
-                            show: true,
-                            indent: 15,
-                            strict: false,
-                            expandedKeys: false
-                        }
-                    });
-                    // 寮圭獥涓嶅嚭鐜版粴鍔ㄦ潯
-                    $(layero).children('.layui-layer-content').css('overflow', 'visible');
-                    layui.form.render('select');
-                }
-            });
-        }
-
-        /* 鍒犻櫎 */
-        function doDel(obj) {
-            layer.confirm('纭畾瑕佸垹闄ら�変腑鏁版嵁鍚楋紵', {
-                skin: 'layui-layer-admin',
-                shade: .1
-            }, function (i) {
-                layer.close(i);
-                var loadIndex = layer.load(2);
-                var ids;
-                if (obj.data) {
-                    ids = [];
-                    ids[0] = obj.data.id;
-                } else {
-                    ids = obj.ids;
-                }
-                $.ajax({
-                    url: baseUrl+"/resource/delete/auth",
-                    headers: {'token': localStorage.getItem('token')},
-                    data: {ids: ids},
-                    method: 'POST',
-                    success: function (res) {
-                        layer.close(loadIndex);
-                        if (res.code === 200){
-                            layer.msg(res.msg, {icon: 1});
-                            insTb.refresh();
-                        } else if (res.code === 403){
-                            top.location.href = baseUrl+"/";
-                        } else {
-                            layer.msg(res.msg, {icon: 2});
-                        }
-                    }
-                })
-            });
-        }
-
-    });
-</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/resource/resource.js?v=20260310_resource_vue" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/role/role.html b/src/main/webapp/views/role/role.html
index b0635fe..79084bd 100644
--- a/src/main/webapp/views/role/role.html
+++ b/src/main/webapp/views/role/role.html
@@ -1,59 +1,346 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>瑙掕壊绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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 {
+            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;
+        }
+
+        .toolbar-bar {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            gap: 8px;
+            flex-wrap: wrap;
+            padding: 12px 16px 10px;
+            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
+        }
+
+        .toolbar-left,
+        .toolbar-right {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .toolbar-title {
+            font-size: 13px;
+            color: #7a8a9f;
+        }
+
+        .toolbar-title strong {
+            color: var(--text-main);
+            font-size: 15px;
+            margin-right: 8px;
+        }
+
+        .toolbar-search {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .search-item {
+            width: 220px;
+        }
+
+        .toolbar-bar .el-input__inner,
+        .toolbar-bar .el-button {
+            height: 32px;
+            line-height: 32px;
+        }
+
+        .toolbar-bar .el-button {
+            padding: 0 12px;
+            border-radius: 8px;
+        }
+
+        .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;
+        }
+
+        .pager-bar {
+            padding: 0 16px 16px;
+            display: flex;
+            justify-content: flex-end;
+        }
+
+        .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 10px;
+        }
+
+        .dialog-footer {
+            display: flex;
+            justify-content: flex-end;
+            gap: 10px;
+        }
+
+        .tree-shell {
+            max-height: 560px;
+            overflow: auto;
+            padding: 8px 4px 0 0;
+        }
+
+        .tree-shell .el-tree-node__content {
+            height: 34px;
+        }
+
+        @media (max-width: 900px) {
+            .page-shell {
+                padding: 10px;
+            }
+
+            .toolbar-bar,
+            .table-wrap,
+            .pager-bar {
+                padding-left: 12px;
+                padding-right: 12px;
+            }
+
+            .search-item {
+                width: 100%;
+            }
+        }
+    </style>
 </head>
 <body>
-
-<!-- 鎼滅储鏍� -->
-<div id="search-box" class="layui-form layui-card-header">
-    <div class="layui-inline" >
-        <div class="layui-input-inline cool-auto-complete">
-            <input id="leader" name="leader" class="layui-input" type="text" style="display: none">
-            <input id="leader$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="涓婄骇" onfocus=this.blur()>
-            <div class="cool-auto-complete-window">
-                <input class="cool-auto-complete-window-input" data-key="roleQueryByleader" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                <select class="cool-auto-complete-window-select" data-key="roleQueryByleaderSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                </select>
+<div id="app" class="page-shell" v-cloak>
+    <section class="card-shell">
+        <div class="toolbar-bar">
+            <div class="toolbar-left">
+                <div class="toolbar-title"><strong>瑙掕壊绠$悊</strong></div>
+                <div class="toolbar-search">
+                    <el-select
+                        v-model="searchForm.leader"
+                        class="search-item"
+                        clearable
+                        filterable
+                        remote
+                        reserve-keyword
+                        placeholder="璇烽�夋嫨涓婄骇"
+                        :remote-method="searchLeaderOptions"
+                        :loading="leaderSearchLoading">
+                        <el-option
+                            v-for="item in leaderSearchOptions"
+                            :key="'search-' + item.id"
+                            :label="item.value"
+                            :value="item.id">
+                        </el-option>
+                    </el-select>
+                    <el-button size="small" type="primary" icon="el-icon-search" @click="handleSearch">鎼滅储</el-button>
+                    <el-button size="small" plain icon="el-icon-refresh-left" @click="handleReset">閲嶇疆</el-button>
+                </div>
+            </div>
+            <div class="toolbar-right">
+                <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-button size="small" plain icon="el-icon-download" :loading="exportLoading" @click="exportRows">瀵煎嚭</el-button>
+                <el-button size="small" plain icon="el-icon-refresh" :loading="loading" @click="loadList">鍒锋柊</el-button>
             </div>
         </div>
-    </div>
-    <!-- 寰呮坊鍔� -->
-    <div id="data-search-btn" class="layui-btn-container layui-form-item">
-        <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>
-        <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>
-    </div>
+
+        <div class="table-wrap">
+            <div class="table-shell">
+                <el-table
+                    ref="dataTable"
+                    v-loading="loading"
+                    :data="tableData"
+                    border
+                    stripe
+                    :height="tableHeight"
+                    @selection-change="handleSelectionChange">
+                    <el-table-column type="selection" width="52" align="center"></el-table-column>
+                    <el-table-column prop="id" label="ID" width="90" align="center"></el-table-column>
+                    <el-table-column prop="code" label="缂栫爜" min-width="180" show-overflow-tooltip></el-table-column>
+                    <el-table-column prop="name" label="鍚嶇О" min-width="180" show-overflow-tooltip></el-table-column>
+                    <el-table-column prop="leader$" label="涓婄骇" min-width="180" show-overflow-tooltip>
+                        <template slot-scope="scope">
+                            {{ scope.row['leader$'] || '--' }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="鎿嶄綔" width="160" fixed="right" align="center">
+                        <template slot-scope="scope">
+                            <el-button type="text" @click="openPowerDialog(scope.row)">鏉冮檺</el-button>
+                            <el-button type="text" @click="openEditDialog(scope.row)">缂栬緫</el-button>
+                        </template>
+                    </el-table-column>
+                </el-table>
+            </div>
+        </div>
+
+        <div class="pager-bar">
+            <el-pagination
+                background
+                layout="total, sizes, prev, pager, next, jumper"
+                :current-page="pagination.curr"
+                :page-sizes="[16, 30, 50, 100, 200, 500]"
+                :page-size="pagination.limit"
+                :total="pagination.total"
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange">
+            </el-pagination>
+        </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="roleDialog.mode === 'create' ? '鏂板瑙掕壊' : '缂栬緫瑙掕壊'"
+        :visible.sync="roleDialog.visible"
+        width="640px"
+        :close-on-click-modal="false">
+        <el-form
+            ref="roleForm"
+            :model="roleForm"
+            :rules="roleRules"
+            label-width="90px"
+            size="small">
+            <el-row :gutter="16">
+                <el-col :span="12">
+                    <el-form-item label="缂栫爜" prop="code">
+                        <el-input v-model.trim="roleForm.code" placeholder="璇疯緭鍏ョ紪鐮�"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="鍚嶇О" prop="name">
+                        <el-input v-model.trim="roleForm.name" placeholder="璇疯緭鍏ュ悕绉�"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                    <el-form-item label="涓婄骇" prop="leader">
+                        <el-select
+                            v-model="roleForm.leader"
+                            clearable
+                            filterable
+                            remote
+                            reserve-keyword
+                            placeholder="璇烽�夋嫨涓婄骇"
+                            style="width: 100%;"
+                            :remote-method="searchDialogLeaderOptions"
+                            :loading="dialogLeaderLoading">
+                            <el-option
+                                v-for="item in dialogLeaderOptions"
+                                :key="'dialog-' + item.id"
+                                :label="item.value"
+                                :value="item.id">
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button @click="roleDialog.visible = false">鍙栨秷</el-button>
+            <el-button type="primary" :loading="roleDialog.submitting" @click="submitRole">淇濆瓨</el-button>
+        </div>
+    </el-dialog>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="powerDialog.roleName ? powerDialog.roleName + ' 鏉冮檺鍒嗛厤' : '鏉冮檺鍒嗛厤'"
+        :visible.sync="powerDialog.visible"
+        width="620px"
+        :close-on-click-modal="false">
+        <div v-loading="powerDialog.loading" class="tree-shell">
+            <el-tree
+                ref="powerTree"
+                node-key="id"
+                show-checkbox
+                :data="powerTreeData"
+                :props="{ label: 'title', children: 'children' }"
+                :default-expanded-keys="powerExpandedKeys"
+                empty-text="鏆傛棤鏉冮檺鏁版嵁">
+            </el-tree>
+        </div>
+        <div slot="footer" class="dialog-footer">
+            <el-button @click="powerDialog.visible = false">鍏抽棴</el-button>
+            <el-button type="primary" :loading="powerDialog.submitting" @click="submitPower">淇濆瓨</el-button>
+        </div>
+    </el-dialog>
 </div>
 
-<!-- 琛ㄦ牸 -->
-<table class="layui-hide" id="role" lay-filter="role"></table>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-xs layui-btn-warm" lay-event="power">鏉冮檺</a>
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/role/role.js" charset="utf-8"></script>
-
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/role/role.js?v=20260310_role_vue_custom" charset="utf-8"></script>
 </body>
 </html>
diff --git a/src/main/webapp/views/role/role_detail.html b/src/main/webapp/views/role/role_detail.html
deleted file mode 100644
index a3eb7a2..0000000
--- a/src/main/webapp/views/role/role_detail.html
+++ /dev/null
@@ -1,81 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="code" class="layui-input" type="text" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鍚嶃��銆�绉帮細</label>
-            <div class="layui-input-inline">
-                <input id="name" class="layui-input" type="text" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">涓娿��銆�绾э細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="leader" class="layui-input" type="text" style="display: none">
-                <input id="leader$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="roleQueryByleader" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="roleQueryByleaderSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="display: none; width:31%;">
-            <label class="layui-form-label">瑙掕壊绛夌骇锛�</label>
-            <div class="layui-input-inline">
-                <select id="level">
-                    <option value="" style="display: none"></option>
-                    <option value="1">涓�绾�</option>
-                    <option value="2">浜岀骇</option>
-                    <option value="3">涓夌骇</option>
-                    <option value="4">鍥涚骇</option>
-                    <option value="5">浜旂骇</option>
-                </select>
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇濆瓨</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/role/role.js" charset="utf-8"></script>
-</html>
diff --git a/src/main/webapp/views/user/user.html b/src/main/webapp/views/user/user.html
index 0e801b4..0c41eae 100644
--- a/src/main/webapp/views/user/user.html
+++ b/src/main/webapp/views/user/user.html
@@ -1,177 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>User 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/originTable.css" media="all">
+    <link rel="stylesheet" href="../../static/vue/element/element.css">
+    <link rel="stylesheet" href="../../static/css/cool.css">
     <style>
-        body {
-            color: #595959;
-            background-color: #f5f7f9;
+        :root {
+            --card-bg: rgba(255, 255, 255, 0.94);
+            --card-border: rgba(216, 226, 238, 0.95);
+            --text-main: #243447;
         }
 
-        .admin-form {
-            padding: 25px 30px 0 0 !important;
-            margin: 0 !important;
+        [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>
-<!-- 姝f枃寮�濮� -->
-<div class="layui-fluid" style="padding-bottom: 0;">
-    <div class="layui-row layui-col-space15">
-        <div class="layui-col-md12">
-            <div class="layui-card">
-                <div class="layui-card-body" style="padding: 10px;">
-                    <form class="layui-form toolbar">
-                        <div class="layui-form-item">
-                            <div class="layui-inline">
-                                <label class="layui-form-label">鐢ㄦ埛鍚�:</label>
-                                <div class="layui-input-inline">
-                                    <input name="username" class="layui-input" placeholder="杈撳叆鐢ㄦ埛鍚�"/>
-                                </div>
+<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 class="layui-inline">
-                                <label class="layui-form-label">鎵嬫満鍙�:</label>
-                                <div class="layui-input-inline">
-                                    <input name="mobile" class="layui-input" placeholder="杈撳叆鎵嬫満鍙�"/>
-                                </div>
-                            </div>
-                            <div class="layui-inline">&emsp;
-                                <button class="layui-btn icon-btn" lay-filter="userTbSearch" lay-submit>
-                                    <i class="layui-icon">&#xe615;</i>鎼滅储
-                                </button>
+                            <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>
-                    </form>
-                    <table id="userTable" lay-filter="userTable"></table>
+                        <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>
-    </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 User' : '淇敼 User'"
+        :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/html" id="hostTpl">
-    <span name="hostName" class="layui-badge layui-badge-gray">{{d.hostName}}</span>
-</script>
-
-<script type="text/html" id="statusTpl">
-    <input type="checkbox" name="status" value="{{d.status}}" lay-skin="switch" lay-text="姝e父|绂佺敤" lay-filter="statusSwitch" {{ d.status === 1 ? 'checked' : '' }}>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-    <a class="layui-btn layui-btn-xs btn-edit layui-btn-warm" lay-event="resetPwd">閲嶇疆瀵嗙爜</a>
-</script>
-
-<script type="text/html" id="userToolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm layui-btn-normal" id="btn-add" lay-event="add"><i class="layui-icon">&#xe654;</i>鏂板</button>
-        <button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete" lay-event="del"><i class="layui-icon">&#xe640;</i>鍒犻櫎</button>
-    </div>
-</script>
-
-<!-- 閲嶇疆瀵嗙爜 -->
-<script type="text/html" id="resetpwd-window">
-    <form class="layui-form model-form">
-        <input type="hidden" id="resetUserId" name="resetUserId">
-        <div class="layui-form-item">
-            <label class="layui-form-label layui-form-required">鏂板瘑鐮�:</label>
-            <div class="layui-input-block">
-                <input id="resetPassword" name="resetPassword" placeholder="璇疯緭鍏ユ柊瀵嗙爜" class="layui-input" lay-verify="required" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-form-item text-right">
-            <button class="layui-btn layui-btn-normal" id="savePwd" lay-filter="savePwd" lay-submit="">纭畾</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
-
-<!-- 琛ㄥ崟寮圭獥 -->
-<script type="text/html" id="editDialog">
-    <form id="detail" lay-filter="detail" class="layui-form admin-form">
-        <input name="id" type="hidden">
-        <input name="status" type="hidden">
-        <div class="layui-row">
-
-            <div class="layui-col-md6">
-                <div class="layui-form-item">
-                    <label class="layui-form-label layui-form-required">鐧诲綍璐︽埛</label>
-                    <div class="layui-input-block">
-                        <input name="username" placeholder="璇疯緭鍏ョ櫥褰曡处鎴�" class="layui-input" lay-vertype="tips" lay-verify="required" required="">
-                    </div>
-                </div>
-
-                <div class="layui-form-item">
-                    <label class="layui-form-label">瑙掕壊锛�</label>
-                    <div class="layui-input-block cool-auto-complete">
-                        <input name="roleId" class="layui-input" style="display: none">
-                        <input id="roleName" name="roleName" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇烽�夋嫨瑙掕壊" onfocus=this.blur()>
-                        <div class="cool-auto-complete-window">
-                            <input class="cool-auto-complete-window-input" data-key="roleQuery" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                            <select class="cool-auto-complete-window-select" data-key="roleQuerySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                            </select>
-                        </div>
-                    </div>
-                </div>
-            </div>
-
-            <div class="layui-col-md6">
-                <div class="layui-form-item">
-                    <label class="layui-form-label layui-form-required">鎵嬫満鍙�</label>
-                    <div class="layui-input-block">
-                        <input name="mobile" placeholder="璇疯緭鍏ユ墜鏈哄彿" class="layui-input" lay-vertype="tips" lay-verify="required" required="">
-                    </div>
-                </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">閭</label>
-                    <div class="layui-input-block">
-                        <input name="email" placeholder="璇疯緭鍏ラ偖绠�" class="layui-input">
-                    </div>
-                </div>
-
-            </div>
-        </div>
-        <hr class="layui-bg-gray">
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/tools/md5.js"></script>
-<script type="text/javascript" src="../../static/js/user/user.js" charset="utf-8"></script>
-
-<!-- 椤圭洰缂栬緫绐楀彛 -->
-<script type="text/html" id="hostEditDialog">
-    <form id="hostEditForm" lay-filter="hostEditForm" class="layui-form model-form">
-        <input name="id" type="hidden"/>
-        <div class="layui-form-item">
-            <label class="layui-form-label layui-form-required">椤圭洰鍚嶇О:</label>
-            <div class="layui-input-block">
-                <input name="name" placeholder="璇疯緭鍏ョ被鍨嬪悕绉�" class="layui-input"
-                       lay-verType="tips" lay-verify="required" required/>
-            </div>
-        </div>
-        <div class="layui-form-item text-right">
-            <button class="layui-btn" lay-filter="hostEditSubmit" lay-submit>淇濆瓨</button>
-            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">鍙栨秷</button>
-        </div>
-    </form>
-</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/user/user.js?v=20260310" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/user/user_detail.html b/src/main/webapp/views/user/user_detail.html
deleted file mode 100644
index c3f9ca7..0000000
--- a/src/main/webapp/views/user/user_detail.html
+++ /dev/null
@@ -1,87 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form" style="text-align: center">
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-<!--        <div class="layui-inline"  style="width:31%;display: none">-->
-<!--            <label class="layui-form-label"><span class="not-null">*</span>鎺堟潈鍟嗘埛锛�</label>-->
-<!--            <div class="layui-input-inline cool-auto-complete">-->
-<!--                <input id="hostId" class="layui-input" type="text" placeholder="鎺堟潈鍟嗘埛" lay-verify="required"  style="display: none">-->
-<!--                <input id="hostName" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="鎺堟潈鍟嗘埛" onfocus=this.blur()>-->
-<!--                <div class="cool-auto-complete-window">-->
-<!--                    <input class="cool-auto-complete-window-input" data-key="hostQuery" onkeyup="autoLoad(this.getAttribute('data-key'))">-->
-<!--                    <select class="cool-auto-complete-window-select" data-key="hostQuerySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">-->
-<!--                    </select>-->
-<!--                </div>-->
-<!--            </div>-->
-<!--        </div>-->
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label"><span class="not-null">*</span>甯愩��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="mobile" class="layui-input" type="text" placeholder="璐﹀彿" lay-verify="required" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鍚嶃��銆�绉帮細</label>
-            <div class="layui-input-inline">
-                <input id="username" class="layui-input" type="text" placeholder="鍚嶇О" lay-verify="required"  autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label">瀵嗐��銆�鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="password" class="layui-input" type="text" placeholder="瀵嗙爜" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:80%;">
-            <label class="layui-form-label"><span class="not-null">*</span>瑙掋��銆�鑹诧細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="roleId" class="layui-input" type="text" placeholder="瑙掕壊" lay-verify="required"  style="display: none">
-                <input id="roleName" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="瑙掕壊" onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="roleQuery" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="roleQuerySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇濆瓨</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/tools/md5.js"></script>
-<script type="text/javascript" src="../../static/js/user/user.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/userLogin/userLogin.html b/src/main/webapp/views/userLogin/userLogin.html
index f63c53a..d28644c 100644
--- a/src/main/webapp/views/userLogin/userLogin.html
+++ b/src/main/webapp/views/userLogin/userLogin.html
@@ -1,61 +1,407 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>鍑瘉璁板綍</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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 {
+            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;
+        }
+
+        .toolbar-bar {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            gap: 8px;
+            flex-wrap: wrap;
+            padding: 12px 16px 10px;
+            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
+        }
+
+        .toolbar-left,
+        .toolbar-right {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .toolbar-title {
+            font-size: 13px;
+            color: #7a8a9f;
+        }
+
+        .toolbar-title strong {
+            color: var(--text-main);
+            font-size: 15px;
+            margin-right: 8px;
+        }
+
+        .toolbar-search {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
+
+        .search-item.user {
+            width: 220px;
+        }
+
+        .toolbar-bar .el-input__inner,
+        .toolbar-bar .el-button {
+            height: 32px;
+            line-height: 32px;
+        }
+
+        .toolbar-bar .el-button {
+            padding: 0 12px;
+            border-radius: 8px;
+        }
+
+        .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;
+        }
+
+        .action-link {
+            color: #2f6bcb;
+            cursor: pointer;
+        }
+
+        .pager-bar {
+            padding: 0 16px 16px;
+            display: flex;
+            justify-content: flex-end;
+        }
+
+        .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 10px;
+        }
+
+        .dialog-footer {
+            display: flex;
+            justify-content: flex-end;
+            gap: 10px;
+        }
+
+        .detail-block {
+            display: grid;
+            grid-template-columns: repeat(2, minmax(0, 1fr));
+            gap: 14px 18px;
+        }
+
+        .detail-item {
+            min-width: 0;
+        }
+
+        .detail-item.full {
+            grid-column: 1 / -1;
+        }
+
+        .detail-label {
+            font-size: 12px;
+            color: #7c8a9d;
+            margin-bottom: 6px;
+        }
+
+        .detail-value {
+            min-height: 32px;
+            padding: 8px 10px;
+            border: 1px solid rgba(220, 229, 238, 0.95);
+            border-radius: 10px;
+            background: #f9fbfd;
+            color: var(--text-main);
+            word-break: break-all;
+        }
+
+        @media (max-width: 900px) {
+            .page-shell {
+                padding: 10px;
+            }
+
+            .toolbar-bar,
+            .table-wrap,
+            .pager-bar {
+                padding-left: 12px;
+                padding-right: 12px;
+            }
+
+            .search-item.user {
+                width: 100%;
+            }
+
+            .detail-block {
+                grid-template-columns: 1fr;
+            }
+        }
+    </style>
 </head>
 <body>
-
-<!-- 鎼滅储鏍� -->
-<div id="search-box" class="layui-form layui-card-header">
-    <div class="layui-inline">
-        <div class="layui-input-inline cool-auto-complete">
-            <input id="userId" class="layui-input" name="user_id" type="text" placeholder="璇疯緭鍏�" autocomplete="off" style="display: none">
-            <input id="userUsername" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="鐢ㄦ埛" onfocus=this.blur()>
-            <div class="cool-auto-complete-window">
-                <input class="cool-auto-complete-window-input" data-key="userQuery" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                <select class="cool-auto-complete-window-select" data-key="userQuerySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                </select>
+<div id="app" class="page-shell" v-cloak>
+    <section class="card-shell">
+        <div class="toolbar-bar">
+            <div class="toolbar-left">
+                <div class="toolbar-title"><strong>鍑瘉璁板綍</strong></div>
+                <div class="toolbar-search">
+                    <el-select
+                        v-model="searchForm.userId"
+                        class="search-item user"
+                        clearable
+                        filterable
+                        remote
+                        reserve-keyword
+                        placeholder="鐢ㄦ埛"
+                        :remote-method="searchUserOptions"
+                        :loading="userSearchLoading">
+                        <el-option
+                            v-for="item in userSearchOptions"
+                            :key="'search-' + item.id"
+                            :label="item.value"
+                            :value="item.id">
+                        </el-option>
+                    </el-select>
+                    <el-button size="small" type="primary" icon="el-icon-search" @click="handleSearch">鎼滅储</el-button>
+                    <el-button size="small" plain icon="el-icon-refresh-left" @click="handleReset">閲嶇疆</el-button>
+                </div>
+            </div>
+            <div class="toolbar-right">
+                <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-button size="small" plain icon="el-icon-download" :loading="exportLoading" @click="exportRows">瀵煎嚭</el-button>
+                <el-button size="small" plain icon="el-icon-refresh" :loading="loading" @click="loadList">鍒锋柊</el-button>
             </div>
         </div>
-    </div>
 
-    <!-- 寰呮坊鍔� -->
-    <div id="data-search-btn" class="layui-btn-container layui-form-item">
-        <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>
-        <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>
-    </div>
+        <div class="table-wrap">
+            <div class="table-shell">
+                <el-table
+                    ref="dataTable"
+                    v-loading="loading"
+                    :data="tableData"
+                    border
+                    stripe
+                    :height="tableHeight"
+                    @selection-change="handleSelectionChange">
+                    <el-table-column type="selection" width="52" align="center"></el-table-column>
+                    <el-table-column prop="id" label="ID" width="90" align="center"></el-table-column>
+                    <el-table-column label="鍛樺伐" min-width="180" show-overflow-tooltip>
+                        <template slot-scope="scope">
+                            <span
+                                v-if="scope.row.userId && scope.row.userUsername"
+                                class="action-link"
+                                @click="openUserDetail(scope.row)">
+                                {{ scope.row.userUsername }}
+                            </span>
+                            <span v-else>--</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="token" label="鍑瘉鍊�" min-width="300" show-overflow-tooltip></el-table-column>
+                    <el-table-column label="娣诲姞鏃堕棿" min-width="180" show-overflow-tooltip>
+                        <template slot-scope="scope">
+                            {{ scope.row['createTime$'] || '--' }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="鎿嶄綔" width="150" fixed="right" align="center">
+                        <template slot-scope="scope">
+                            <el-button type="text" @click="openDetailDialog(scope.row)">璇︽儏</el-button>
+                            <el-button type="text" @click="openEditDialog(scope.row)">缂栬緫</el-button>
+                        </template>
+                    </el-table-column>
+                </el-table>
+            </div>
+        </div>
+
+        <div class="pager-bar">
+            <el-pagination
+                background
+                layout="total, sizes, prev, pager, next, jumper"
+                :current-page="pagination.curr"
+                :page-sizes="[16, 30, 50, 100, 200, 500]"
+                :page-size="pagination.limit"
+                :total="pagination.total"
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange">
+            </el-pagination>
+        </div>
+    </section>
+
+    <el-dialog
+        class="dialog-panel"
+        :title="loginDialog.mode === 'create' ? '鏂板鍑瘉' : loginDialog.readonly ? '鍑瘉璇︽儏' : '缂栬緫鍑瘉'"
+        :visible.sync="loginDialog.visible"
+        width="760px"
+        :close-on-click-modal="false">
+        <el-form
+            ref="loginForm"
+            :model="loginForm"
+            :rules="loginRules"
+            label-width="90px"
+            size="small">
+            <el-row :gutter="16">
+                <el-col :span="12">
+                    <el-form-item label="鍛樺伐" prop="userId">
+                        <el-select
+                            v-model="loginForm.userId"
+                            clearable
+                            filterable
+                            remote
+                            reserve-keyword
+                            style="width: 100%;"
+                            placeholder="璇烽�夋嫨鍛樺伐"
+                            :disabled="loginDialog.readonly"
+                            :remote-method="searchDialogUserOptions"
+                            :loading="dialogUserLoading">
+                            <el-option
+                                v-for="item in dialogUserOptions"
+                                :key="'dialog-' + item.id"
+                                :label="item.value"
+                                :value="item.id">
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="鍑瘉鍊�" prop="token">
+                        <el-input v-model.trim="loginForm.token" :disabled="loginDialog.readonly" placeholder="璇疯緭鍏ュ嚟璇佸��"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="娣诲姞鏃堕棿" prop="createTime">
+                        <el-date-picker
+                            v-model="loginForm.createTime"
+                            type="datetime"
+                            value-format="yyyy-MM-dd HH:mm:ss"
+                            style="width: 100%;"
+                            :disabled="loginDialog.readonly"
+                            placeholder="璇烽�夋嫨娣诲姞鏃堕棿">
+                        </el-date-picker>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button @click="loginDialog.visible = false">{{ loginDialog.readonly ? '鍏抽棴' : '鍙栨秷' }}</el-button>
+            <el-button v-if="!loginDialog.readonly" type="primary" :loading="loginDialog.submitting" @click="submitLogin">淇濆瓨</el-button>
+        </div>
+    </el-dialog>
+
+    <el-dialog
+        class="dialog-panel"
+        title="鍛樺伐璇︽儏"
+        :visible.sync="userDetail.visible"
+        width="720px"
+        :close-on-click-modal="false">
+        <div class="detail-block">
+            <div class="detail-item">
+                <div class="detail-label">缂栧彿</div>
+                <div class="detail-value">{{ userDetail.data.id || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">璐﹀彿</div>
+                <div class="detail-value">{{ userDetail.data.mobile || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">鍚嶇О</div>
+                <div class="detail-value">{{ userDetail.data.username || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">瑙掕壊</div>
+                <div class="detail-value">{{ userDetail.data.roleName || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">娉ㄥ唽鏃堕棿</div>
+                <div class="detail-value">{{ userDetail.data['createTime$'] || '--' }}</div>
+            </div>
+            <div class="detail-item">
+                <div class="detail-label">鐘舵��</div>
+                <div class="detail-value">{{ userDetail.data['status$'] || '--' }}</div>
+            </div>
+        </div>
+        <div slot="footer" class="dialog-footer">
+            <el-button @click="userDetail.visible = false">鍏抽棴</el-button>
+        </div>
+    </el-dialog>
 </div>
 
-<!-- 琛ㄦ牸 -->
-<table class="layui-hide" id="userLogin" lay-filter="userLogin"></table>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">璇︽儏</a>
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-</script>
-
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/userLogin/userLogin.js" charset="utf-8"></script>
-
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/userLogin/userLogin.js?v=20260310_userlogin_vue_custom" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/userLogin/userLogin_detail.html b/src/main/webapp/views/userLogin/userLogin_detail.html
deleted file mode 100644
index dad6154..0000000
--- a/src/main/webapp/views/userLogin/userLogin_detail.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鍛樸��銆�宸ワ細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="userId" class="layui-input" type="text" placeholder="鍛樺伐" lay-verify="required" style="display: none">
-                <input id="userUsername" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="鍛樺伐" onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="userQuery" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="userQuerySelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>鍑� 璇� 鍊硷細</label>
-            <div class="layui-input-inline">
-                <input id="token" class="layui-input" type="text" placeholder="鍑瘉鍊�" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>娣诲姞鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="createTime$" class="layui-input" type="text" placeholder="娣诲姞鏃堕棿" lay-verify="required" >
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇濆瓨</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/userLogin/userLogin.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/watch/console.html b/src/main/webapp/views/watch/console.html
index 1656a0b..5abc253 100644
--- a/src/main/webapp/views/watch/console.html
+++ b/src/main/webapp/views/watch/console.html
@@ -507,7 +507,6 @@
 					}
 				</style>
 				<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-				<script type="text/javascript" src="../../static/layui/layui.js"></script>
 				<script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script>
 			<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
 			<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
@@ -860,18 +859,6 @@
 							window.ELEMENT.Message({
 								message: message,
 								type: type || 'info'
-							});
-							return;
-						}
-						if (window.layer && typeof window.layer.msg === 'function') {
-							const iconMap = {
-								success: 1,
-								error: 2,
-								warning: 0
-							};
-							window.layer.msg(message, {
-								icon: iconMap[type] != null ? iconMap[type] : 0,
-								time: 1800
 							});
 							return;
 						}
diff --git a/src/main/webapp/views/wrkLastno/wrkLastno.html b/src/main/webapp/views/wrkLastno/wrkLastno.html
index ded83ea..6a3ea11 100644
--- a/src/main/webapp/views/wrkLastno/wrkLastno.html
+++ b/src/main/webapp/views/wrkLastno/wrkLastno.html
@@ -1,58 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>WrkLastno 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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>
 
-<!-- 鎼滅储鏍� -->
-<div id="search-box" class="layui-form layui-card-header">
-    <!--<div class="layui-inline">-->
-        <!--<label class="layui-form-label">缂栥��銆�鍙凤細</label>-->
-        <!--<div class="layui-input-inline">-->
-            <!--<input class="layui-input" type="text" name="id" placeholder="璇疯緭鍏�" autocomplete="off">-->
-        <!--</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>
 
-    <!--&lt;!&ndash; 寰呮坊鍔� &ndash;&gt;-->
-    <!--<div id="data-search-btn" class="layui-btn-container layui-form-item">-->
-        <!--<button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>-->
-        <!--<button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>-->
-    <!--</div>-->
+            <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' ? '鏂板 WrkLastno' : '淇敼 WrkLastno'"
+        :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>
-
-<!-- 琛ㄦ牸 -->
-<div class="layui-form">
-    <table class="layui-hide" id="wrkLastno" lay-filter="wrkLastno"></table>
-</div>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-sm" id="btn-add" lay-event="addData">鏂板</button>
-        <button class="layui-btn layui-btn-sm" id="btn-delete" lay-event="deleteData">鍒犻櫎</button>
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-    <!--<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">璇︽儏</a>-->
-    <a class="layui-btn layui-btn-xs btn-edit" lay-event="edit">缂栬緫</a>
-</script>
 
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/wrkLastno/wrkLastno.js" charset="utf-8"></script>
-
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/wrkLastno/wrkLastno.js?v=20260310" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/wrkLastno/wrkLastno_detail.html b/src/main/webapp/views/wrkLastno/wrkLastno_detail.html
deleted file mode 100644
index f2ac0c9..0000000
--- a/src/main/webapp/views/wrkLastno/wrkLastno_detail.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-    <!--
-        <div class="layui-inline"  style="display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" placeholder="缂栧彿">
-            </div>
-        </div>
-    -->
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>绫汇��銆�鍨嬶細</label>
-            <div class="layui-input-inline">
-                <input id="wrkMk" class="layui-input" type="text" onkeyup="check(this.id, 'wrkLastno')">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>褰撳墠ID锛�</label>
-            <div class="layui-input-inline">
-                <input id="wrkNo" class="layui-input" type="text" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>缁堟ID锛�</label>
-            <div class="layui-input-inline">
-                <input id="eno" class="layui-input" type="text" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>璧峰ID锛�</label>
-            <div class="layui-input-inline">
-                <input id="sno" class="layui-input" type="text" lay-verify="required" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">澶囥��銆�娉細</label>
-            <div class="layui-input-inline">
-                <input id="memoM" class="layui-input" type="text">
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/wrkLastno/wrkLastno.js" charset="utf-8"></script>
-</html>
-
diff --git a/src/main/webapp/views/wrkMast/wrkMast.html b/src/main/webapp/views/wrkMast/wrkMast.html
index 40e8c5b..390505e 100644
--- a/src/main/webapp/views/wrkMast/wrkMast.html
+++ b/src/main/webapp/views/wrkMast/wrkMast.html
@@ -6,11 +6,13 @@
 		<title>浠诲姟绠$悊</title>
 		<link rel="stylesheet" href="../../static/vue/element/element.css">
 		<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-		<script type="text/javascript" src="../../static/layui/layui.js"></script>
 		<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
 		<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
 		<script type="text/javascript" src="../../static/vue/element/element.js"></script>
 		<style>
+			[v-cloak] {
+				display: none;
+			}
 			.el-table .success-row {
 				background: #b6ff8e;
 			}
@@ -18,7 +20,7 @@
 	</head>
 
 	<body>
-		<div id="app" style="display: flex;justify-content: center;flex-wrap: wrap;">
+		<div id="app" v-cloak style="display: flex;justify-content: center;flex-wrap: wrap;">
 			<div style="width: 100%;">
 				<el-card class="box-card">
 					<el-form :inline="true" :model="tableSearchParam" class="demo-form-inline">
@@ -150,10 +152,6 @@
 
 		</div>
 		<script>
-			var $layui = layui.config({
-				base: baseUrl + "/static/wms/layui/lay/modules/"
-			}).use(['layer', 'form'], function() {})
-
 			var app = new Vue({
 				el: '#app',
 				data: {
diff --git a/src/main/webapp/views/wrkMastLog/wrkMastLog.html b/src/main/webapp/views/wrkMastLog/wrkMastLog.html
index 026a843..7eb29b9 100644
--- a/src/main/webapp/views/wrkMastLog/wrkMastLog.html
+++ b/src/main/webapp/views/wrkMastLog/wrkMastLog.html
@@ -1,92 +1,670 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 <head>
     <meta charset="utf-8">
-    <title></title>
+    <title>WrkMastLog 绠$悊</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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
+    <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="search-box" class="layui-form layui-card-header">
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="wrk_no" placeholder="宸ヤ綔鍙�" autocomplete="off">
-        </div>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="wms_wrk_no" placeholder="WMS宸ヤ綔鍙�" autocomplete="off">
-        </div>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline cool-auto-complete">
-            <input id="wrkSts" class="layui-input" name="wrk_sts" type="text" placeholder="璇疯緭鍏�" autocomplete="off" style="display: none">
-            <input id="wrkSts$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="宸ヤ綔鐘舵��" onfocus=this.blur()>
-            <div class="cool-auto-complete-window">
-                <input class="cool-auto-complete-window-input" data-key="basWrkStatusQueryBywrkSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                <select class="cool-auto-complete-window-select" data-key="basWrkStatusQueryBywrkStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                </select>
+<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>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline cool-auto-complete">
-            <input id="ioType" class="layui-input" name="io_type" type="text" placeholder="璇疯緭鍏�" autocomplete="off" style="display: none">
-            <input id="ioType$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="鍏ュ嚭搴撶被鍨�" onfocus=this.blur()>
-            <div class="cool-auto-complete-window">
-                <input class="cool-auto-complete-window-input" data-key="basWrkIotypeQueryByioType" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                <select class="cool-auto-complete-window-select" data-key="basWrkIotypeQueryByioTypeSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                </select>
-            </div>
-        </div>
-    </div>
-    <!-- 鏃ユ湡鑼冨洿 -->
-    <div class="layui-inline" style="width: 300px">
-        <div class="layui-input-inline">
-            <input class="layui-input layui-laydate-range" name="io_time" type="text" placeholder="璧峰鏃堕棿 - 缁堟鏃堕棿" autocomplete="off" style="width: 300px">
-        </div>
-    </div>
-    <div class="layui-inline">
-        <div class="layui-input-inline">
-            <input class="layui-input" type="text" name="condition" placeholder="璇疯緭鍏�" autocomplete="off">
-        </div>
-    </div>
+    </section>
 
-    <!-- 寰呮坊鍔� -->
-    <div id="data-search-btn" class="layui-btn-container layui-form-item">
-        <button id="search" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="search">鎼滅储</button>
-        <button id="reset" class="layui-btn layui-btn-primary layui-btn-radius" lay-submit lay-filter="reset">閲嶇疆</button>
-    </div>
+    <el-dialog
+        class="dialog-panel"
+        :title="dialog.mode === 'create' ? '鏂板 WrkMastLog' : '淇敼 WrkMastLog'"
+        :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>
-
-<!-- 琛ㄦ牸 -->
-<div class="layui-form">
-    <table class="layui-hide" id="wrkMastLog" lay-filter="wrkMastLog"></table>
-</div>
-<script type="text/html" id="toolbar">
-    <div class="layui-btn-container">
-        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData" style="margin-top: 10px">瀵煎嚭</button>
-    </div>
-</script>
-
-<script type="text/html" id="operate">
-<!--    <a class="layui-btn layui-btn-xs btn-detlShow" lay-event="detlShow">鏄庣粏</a>-->
-<!--    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">璇︽儏</a>-->
-</script>
 
 <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/wrkMastLog/wrkMastLog.js" charset="utf-8"></script>
-
-<iframe id="detail-iframe" scrolling="auto" style="display:none;"></iframe>
-
+<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/wrkMastLog/wrkMastLog.js?v=20260310" charset="utf-8"></script>
 </body>
 </html>
-
diff --git a/src/main/webapp/views/wrkMastLog/wrkMastLog_detail.html b/src/main/webapp/views/wrkMastLog/wrkMastLog_detail.html
deleted file mode 100644
index 8e60e8d..0000000
--- a/src/main/webapp/views/wrkMastLog/wrkMastLog_detail.html
+++ /dev/null
@@ -1,196 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <title></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/layui/css/layui.css" media="all">
-    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
-    <link rel="stylesheet" href="../../static/css/common.css" media="all">
-</head>
-<body>
-
-<!-- 璇︽儏 -->
-<div id="data-detail" class="layer_self_wrap">
-    <form id="detail" class="layui-form">
-        <div class="layui-inline"  style="width:31%;display: none">
-            <label class="layui-form-label"><span class="not-null">*</span>缂栥��銆�鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="id" class="layui-input" type="text" onkeyup="check(this.id, 'wrkMastLog')" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label"><span class="not-null">*</span>宸� 浣� 鍙凤細</label>
-            <div class="layui-input-inline">
-                <input id="wrkNo" class="layui-input" type="text" onkeyup="check(this.id, 'wrkMastLog')" lay-verify="required|number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸ヤ綔鐘舵�侊細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="wrkSts" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="wrkSts$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basWrkStatusQueryBywrkSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basWrkStatusQueryBywrkStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍏ュ嚭搴撶被鍨嬶細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="ioType" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="ioType$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basWrkIotypeQueryByioType" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basWrkIotypeQueryByioTypeSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鍫嗗灈鏈哄彿锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="crnNo" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="crnNo$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basCrnpQueryBycrnNo" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basCrnpQueryBycrnNoSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">浼� 鍏� 绾э細</label>
-            <div class="layui-input-inline">
-                <input id="ioPri" class="layui-input" type="text" lay-verify="number" >
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩爣搴撲綅锛�</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="locNo" class="layui-input" type="text" style="display: none">
-                <input id="locNo$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="locMastQueryBylocNo" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="locMastQueryBylocNoSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鐩� 鏍� 绔欙細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="staNo" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="staNo$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basDevpQueryBystaNo" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basDevpQueryBystaNoSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婧愩��銆�绔欙細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="sourceStaNo" class="layui-input" type="text" lay-verify="number"  style="display: none">
-                <input id="sourceStaNo$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="basDevpQueryBysourceStaNo" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="basDevpQueryBysourceStaNoSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婧� 搴� 浣嶏細</label>
-            <div class="layui-input-inline cool-auto-complete">
-                <input id="sourceLocNo" class="layui-input" type="text" style="display: none">
-                <input id="sourceLocNo$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="璇疯緭鍏�..." onfocus=this.blur()>
-                <div class="cool-auto-complete-window">
-                    <input class="cool-auto-complete-window-input" data-key="locMastQueryBysourceLocNo" onkeyup="autoLoad(this.getAttribute('data-key'))">
-                    <select class="cool-auto-complete-window-select" data-key="locMastQueryBysourceLocNoSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
-                    </select>
-                </div>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鎷c��銆�鏂欙細</label>
-            <div class="layui-input-inline">
-                <input id="picking" class="layui-input" type="checkBox" lay-skin="primary" lay-filter='detailCheckbox'>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">閫�銆�銆�鍑猴細</label>
-            <div class="layui-input-inline">
-                <input id="exitMk" class="layui-input" type="checkBox" lay-skin="primary" lay-filter='detailCheckbox'>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">绌恒��銆�鏉匡細</label>
-            <div class="layui-input-inline">
-                <input id="emptyMk" class="layui-input" type="checkBox" lay-skin="primary" lay-filter='detailCheckbox'>
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">宸ヤ綔鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="ioTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鍫嗗灈鏈哄惎鍔ㄦ椂闂达細</label>
-            <div class="layui-input-inline">
-                <input id="crnStrTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label" style="font-size: x-small">鍫嗗灈鏈哄仠姝㈡椂闂达細</label>
-            <div class="layui-input-inline">
-                <input id="crnEndTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鎷f枡鏃堕棿锛�</label>
-            <div class="layui-input-inline">
-                <input id="plcStrTime$" class="layui-input" type="text" autocomplete="off">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">鏉°��銆�鐮侊細</label>
-            <div class="layui-input-inline">
-                <input id="barcode" class="layui-input" type="text">
-            </div>
-        </div>
-        <div class="layui-inline"  style="width:31%;">
-            <label class="layui-form-label">婊°��銆�鏉匡細</label>
-            <div class="layui-input-inline">
-                <input id="fullPlt" class="layui-input" type="checkBox" lay-skin="primary" lay-filter='detailCheckbox'>
-            </div>
-        </div>
-
-
-        <hr class="layui-bg-gray">
-
-        <div id="data-detail-btn" class="layui-btn-container layui-form-item">
-            <div id="data-detail-submit-save" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="save">淇濆瓨</div>
-            <div id="data-detail-submit-edit" type="button" class="layui-btn layui-btn-normal" lay-submit lay-filter="edit">淇敼</div>
-            <div id="data-detail-close" type="button" class="layui-btn" lay-submit lay-filter="close">鍏抽棴</div>
-        </div>
-
-        <div id="prompt">
-            娓╅Θ鎻愮ず锛氳浠旂粏濉啓鐩稿叧淇℃伅锛�<span class="extrude"><span class="not-null">*</span> 涓哄繀濉�夐」銆�</span>
-        </div>
-    </form>
-</div>
-</body>
-<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
-<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
-<script type="text/javascript" src="../../static/js/wrkMastLog/wrkMastLog.js" charset="utf-8"></script>
-</html>
-

--
Gitblit v1.9.1