From 223e200a69aff78811cd93de72f6053532f97d3c Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期三, 18 三月 2026 12:47:27 +0800
Subject: [PATCH] #

---
 src/main/webapp/views/debugParam/debugParam.html | 1040 +++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 776 insertions(+), 264 deletions(-)

diff --git a/src/main/webapp/views/debugParam/debugParam.html b/src/main/webapp/views/debugParam/debugParam.html
index 7dd00a9..94fb9bc 100644
--- a/src/main/webapp/views/debugParam/debugParam.html
+++ b/src/main/webapp/views/debugParam/debugParam.html
@@ -1,299 +1,811 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-CN">
 
-	<head>
-		<meta charset="UTF-8">
-		<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/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>
-			.show-box {
-				width: 20%;
-				display: flex;
-				justify-content: flex-start;
-				align-items: center;
-				margin-bottom: 30px;
-			}
-		</style>
-	</head>
+<head>
+    <meta charset="UTF-8">
+    <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/js/common.js"></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>
+        body {
+            margin: 0;
+            padding: 16px;
+            background: #f5f7fa;
+            color: #303133;
+            box-sizing: border-box;
+        }
 
-	<body>
-		<div id="app">
-			<div>
-				<el-card class="box-card">
-					<div slot="header" class="clearfix">
-						<span>璋冨害鍙傛暟</span>
-					</div>
+        * {
+            box-sizing: border-box;
+        }
 
-					<div style="display: flex;flex-wrap: wrap;">
-						<div class="show-box">
-							<div>璋冨害灏忚溅鍚屽眰鏈�澶ф暟閲�</div>
-							<el-input v-model="codeMap.dispatchShuttleMaxNum" style="width: 60%;"></el-input>
-						</div>
+        .page-shell {
+            max-width: 1600px;
+            margin: 0 auto;
+        }
 
-						<div class="show-box">
-							<div>鍏ュ簱棰勭暀灏忚溅</div>
-							<el-input v-model="codeMap.shuttleWrkInObligateCount" style="width: 60%;"></el-input>
-						</div>
+        .hero-card,
+        .filter-card,
+        .group-card {
+            margin-bottom: 16px;
+        }
 
-						<div class="show-box">
-							<div>閬块殰鍐呭湀鍗婂緞</div>
-							<el-input v-model="codeMap.avoidInnerCircle" style="width: 60%;"></el-input>
-						</div>
+        .hero-header,
+        .group-header,
+        .filter-bar {
+            display: flex;
+            align-items: flex-start;
+            justify-content: space-between;
+            gap: 16px;
+            flex-wrap: wrap;
+        }
 
-						<div class="show-box">
-							<div>閬块殰澶栧湀鍗婂緞</div>
-							<el-input v-model="codeMap.avoidOuterCircle" style="width: 60%;"></el-input>
-						</div>
+        .hero-title {
+            font-size: 20px;
+            font-weight: 600;
+            line-height: 1.2;
+            margin: 0 0 4px;
+        }
 
-						<div class="show-box">
-							<div>鍦板浘姣嶈建鏂瑰悜(x,y)</div>
-							<el-input v-model="codeMap.direction_map" style="width: 60%;"></el-input>
-						</div>
+        .hero-desc,
+        .group-desc {
+            margin: 0;
+            color: #606266;
+            line-height: 1.5;
+        }
 
-                        <div class="show-box">
-                            <div>灏忚溅(x,y)鍛戒护杩愯鏂瑰悜棰犲��</div>
-                            <el-radio border v-model="codeMap.shuttleDirectionReverse" label="Y">寮�</el-radio>
-                            <el-radio border v-model="codeMap.shuttleDirectionReverse" label="N">鍏�</el-radio>
-                        </div>
+        .hero-actions,
+        .group-actions,
+        .row-actions,
+        .group-tags {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
 
-						<div class="show-box">
-							<div>灏忚溅鍑烘彁鍗囨満杩戠偣璺濈</div>
-							<el-input v-model="codeMap.shuttleOutLiftLocationDistance" style="width: 60%;"></el-input>
-						</div>
+        .summary-strip {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 8px;
+            margin-top: 10px;
+        }
 
-						<div class="show-box">
-							<div>灏忚溅绉诲姩杩炵画涓嬪彂鎸囦护</div>
-							<el-radio border v-model="codeMap.shuttleMoveCommandsContinuously" label="Y">寮�</el-radio>
-							<el-radio border v-model="codeMap.shuttleMoveCommandsContinuously" label="N">鍏�</el-radio>
-						</div>
+        .summary-pill {
+            display: inline-flex;
+            align-items: center;
+            gap: 8px;
+            padding: 6px 10px;
+            border-radius: 999px;
+            background: #f7f9fc;
+            border: 1px solid #ebeef5;
+            font-size: 12px;
+            color: #606266;
+            line-height: 1.4;
+        }
 
-						<div class="show-box">
-							<div>鍏佽浜ょ閲嶆柊瑙勫垝璺緞</div>
-							<el-radio border v-model="codeMap.trafficControlRestartCalcPath" label="Y">寮�</el-radio>
-							<el-radio border v-model="codeMap.trafficControlRestartCalcPath" label="N">鍏�</el-radio>
-						</div>
+        .summary-pill strong {
+            font-size: 14px;
+            color: #303133;
+        }
 
-						<div class="show-box">
-							<div>杈撳嚭RCS璋冭瘯鏃ュ織</div>
-							<el-radio border v-model="codeMap.wcsDebugShowLog" label="true">寮�</el-radio>
-							<el-radio border v-model="codeMap.wcsDebugShowLog" label="false">鍏�</el-radio>
-						</div>
-					</div>
+        .filter-item {
+            min-width: 220px;
+            flex: 1 1 260px;
+        }
 
-					<div style="margin-top: 20px;">
-						<el-button type="primary" @click="saveParam('shuttle')">淇濆瓨</el-button>
-					</div>
-				</el-card>
-			</div>
+        .group-title {
+            font-size: 18px;
+            font-weight: 600;
+            margin-bottom: 6px;
+        }
 
-			<div style="margin-top: 20px;">
-				<el-card class="box-card">
-					<div slot="header" class="clearfix">
-						<span>鍏呯數鍙傛暟</span>
-					</div>
+        .code-cell {
+            font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
+            color: #606266;
+            font-size: 12px;
+            line-height: 1.6;
+            word-break: break-all;
+        }
 
-					<div style="display: flex;flex-wrap: wrap;">
-						<div class="show-box">
-							<div>灏忚溅鍏呯數鏈�澶ч槇鍊�</div>
-							<el-input v-model="codeMap.chargeMaxValue" style="width: 60%;"></el-input>
-						</div>
+        .config-name {
+            display: flex;
+            flex-direction: column;
+            gap: 4px;
+            line-height: 1.5;
+        }
 
-						<div class="show-box">
-							<div>灏忚溅鐢甸噺棰勮闃堝��</div>
-							<el-input v-model="codeMap.shuttlePowerEarlyValue" style="width: 60%;"></el-input>
-						</div>
+        .config-name-main {
+            font-weight: 600;
+            color: #303133;
+        }
 
-						<div class="show-box">
-							<div>灏忚溅婊$數鏍″噯</div>
-							<el-radio border v-model="codeMap.shuttleMaxPowerVerify" label="true">寮�</el-radio>
-							<el-radio border v-model="codeMap.shuttleMaxPowerVerify" label="false">鍏�</el-radio>
-						</div>
+        .config-name-sub {
+            font-size: 12px;
+            color: #909399;
+        }
 
-						<div class="show-box">
-							<div>瀹氭椂鍏呯數寮�鍏�</div>
-							<el-radio border v-model="codeMap.timedCharge" label="Y">寮�</el-radio>
-							<el-radio border v-model="codeMap.timedCharge" label="N">鍏�</el-radio>
-						</div>
+        .editor-cell {
+            min-width: 320px;
+        }
 
-						<div class="show-box">
-							<div>瀹氭椂鍏呯數鏃堕棿娈�</div>
-							<el-input v-model="codeMap.timedChargeRange" style="width: 60%;"></el-input>
-						</div>
+        .status-cell {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            flex-wrap: wrap;
+        }
 
-						<div class="show-box">
-							<div>灏忚溅榛樿鍏呯數绾�</div>
-							<el-input v-model="codeMap.shuttleDefaultChargePowerLine" style="width: 60%;"></el-input>
-						</div>
+        .empty-wrap {
+            padding: 32px 0 8px;
+            background: #fff;
+            border-radius: 8px;
+        }
 
-						<div class="show-box">
-							<div>灏忚溅瀹氭椂鍏呯數绾�</div>
-							<el-input v-model="codeMap.timedChargePowerLine" style="width: 60%;"></el-input>
-						</div>
-					</div>
+        .el-card__header {
+            padding: 16px 20px;
+        }
 
-					<div style="margin-top: 20px;">
-						<el-button type="primary" @click="saveParam('charge')">淇濆瓨</el-button>
-					</div>
-				</el-card>
-			</div>
+        .el-card__body {
+            padding: 18px 20px;
+        }
 
-			<div style="margin-top: 20px;">
-				<el-card class="box-card">
-					<div slot="header" class="clearfix">
-						<span>婕旂ず妯″紡鍙傛暟</span>
-					</div>
-					<div style="display: flex;flex-wrap: wrap;">
-						<div class="show-box">
-							<div>绉诲姩婕旂ず妯″紡-妤煎眰</div>
-							<el-input v-model="codeMap.demoRunLev" style="width: 60%;"></el-input>
-						</div>
+        .hero-card .el-card__body {
+            padding: 14px 18px;
+        }
 
-						<div class="show-box">
-							<div>绉诲姩婕旂ず妯″紡-鏄惁鎹㈠眰</div>
-							<el-radio border v-model="codeMap.demoSwitchLev" label="Y">寮�</el-radio>
-							<el-radio border v-model="codeMap.demoSwitchLev" label="N">鍏�</el-radio>
-						</div>
+        @media (max-width: 900px) {
+            body {
+                padding: 12px;
+            }
 
-						<div class="show-box">
-							<div>婕旂ず妯″紡-璐х墿鎼繍</div>
-							<el-radio border v-model="codeMap.demoCargoMove" label="Y">寮�</el-radio>
-							<el-radio border v-model="codeMap.demoCargoMove" label="N">鍏�</el-radio>
-						</div>
+            .editor-cell {
+                min-width: 0;
+            }
+        }
+    </style>
+</head>
 
-                        <div class="show-box">
-                            <div>婕旂ず妯″紡-璺戝簱</div>
-                            <el-radio border v-model="codeMap.demoModeRunLoc" label="Y">寮�</el-radio>
-                            <el-radio border v-model="codeMap.demoModeRunLoc" label="N">鍏�</el-radio>
-                        </div>
+<body>
+<div id="app" class="page-shell" v-loading="loading">
+    <el-card shadow="never" class="hero-card">
+        <div class="hero-header">
+            <div>
+                <h1 class="hero-title">璋冭瘯鍙傛暟</h1>
+                <p class="hero-desc">
+                    鍩轰簬褰撳墠璁惧鎷撴墤鍜� <code>sys_config</code> 瀹炴椂灞曠ず杩愯鍙傛暟銆�
+                </p>
+            </div>
+            <div class="hero-actions">
+                <el-button icon="el-icon-refresh" @click="reloadData">鍒锋柊鏁版嵁</el-button>
+                <el-button type="primary"
+                           icon="el-icon-check"
+                           :loading="saveAllLoading"
+                           :disabled="!hasChanges"
+                           @click="saveAll">
+                    淇濆瓨鍏ㄩ儴鍙樻洿
+                </el-button>
+            </div>
+        </div>
 
-					</div>
+        <div class="summary-strip">
+            <div class="summary-pill" v-for="item in summaryCards" :key="item.key">
+                <span>{{ item.label }}</span>
+                <strong>{{ item.value }}</strong>
+            </div>
+        </div>
+    </el-card>
 
-					<div style="margin-top: 20px;">
-						<el-button type="primary" @click="saveParam('demo')">淇濆瓨</el-button>
-					</div>
-				</el-card>
-			</div>
-		</div>
-		<script>
-			var app = new Vue({
-				el: '#app',
-				data: {
-					codeMap: {},
-				},
-				created() {
-					this.init()
-				},
-				methods: {
-					init() {
-						this.getConfigData()
-					},
-					getConfigData() {
-						let that = this;
-						$.ajax({
-							url: baseUrl + "/config/listAll/auth",
-							headers: {
-								'token': localStorage.getItem('token')
-							},
-							data: {},
-							dataType: 'json',
-							contentType: 'application/json;charset=UTF-8',
-							method: 'GET',
-							success: function(res) {
-								if (res.code == 200) {
-									let codeMap = {}
-									res.data.forEach((item) => {
-										codeMap[item.code] = item.value
-									})
-									that.codeMap = codeMap;
-								} else if (res.code === 403) {
-									top.location.href = baseUrl + "/";
-								} else {
-									that.$message({
-										message: res.msg,
-										type: 'error'
-									});
-								}
-							}
-						});
-					},
-					saveParam(type) {
-						let that = this;
-						let codeList = this.getParamData(type)
-						let updateCodeList = [];
+    <el-card shadow="never" class="filter-card">
+        <div class="filter-bar">
+            <div class="filter-item">
+                <el-input v-model.trim="searchText"
+                          clearable
+                          prefix-icon="el-icon-search"
+                          placeholder="鎸夐厤缃悕绉版垨缂栫爜绛涢��">
+                </el-input>
+            </div>
+            <div style="width: 240px;">
+                <el-select v-model="selectedType" filterable style="width: 100%;">
+                    <el-option label="鍏ㄩ儴鍒嗙粍" value="all"></el-option>
+                    <el-option v-for="type in selectTypeOptions"
+                               :key="type"
+                               :label="getGroupLabel(type)"
+                               :value="type">
+                    </el-option>
+                </el-select>
+            </div>
+            <div>
+                <el-switch v-model="changedOnly" active-text="浠呯湅宸蹭慨鏀�"></el-switch>
+            </div>
+            <div class="group-tags">
+                <el-tag size="small">鍙傛暟 {{ configList.length }} 椤�</el-tag>
+                <el-tag size="small">鍒嗙粍 {{ selectTypeOptions.length }} 涓�</el-tag>
+                <el-tag v-if="hasChanges" type="warning" size="small">寰呬繚瀛� {{ changedCount }} 椤�</el-tag>
+            </div>
+        </div>
+    </el-card>
 
-						codeList.forEach((key) => {
-							let value = this.codeMap[key]
-							updateCodeList.push({
-								code: key,
-								value: value
-							})
-						})
+    <div v-if="filteredGroups.length === 0" class="empty-wrap">
+        <el-empty description="褰撳墠绛涢�夋潯浠朵笅娌℃湁鍙樉绀虹殑鍙傛暟"></el-empty>
+    </div>
 
-						console.log(updateCodeList)
+    <el-card v-for="group in filteredGroups" :key="group.type" shadow="never" class="group-card">
+        <div slot="header" class="group-header">
+            <div>
+                <div class="group-title">{{ group.label }}</div>
+                <p class="group-desc">{{ group.desc }}</p>
+                <div class="group-tags" style="margin-top: 10px;">
+                    <el-tag size="mini">{{ group.items.length }} 椤�</el-tag>
+                    <el-tag v-if="group.deviceText" type="success" size="mini">{{ group.deviceText }}</el-tag>
+                    <el-tag v-if="group.changedCount > 0" type="warning" size="mini">宸蹭慨鏀� {{ group.changedCount }} 椤�</el-tag>
+                </div>
+            </div>
+            <div class="group-actions">
+                <el-button size="mini"
+                           :disabled="group.changedCount === 0"
+                           :loading="saveGroupLoading[group.type]"
+                           type="primary"
+                           @click="saveGroup(group)">
+                    淇濆瓨鏈粍
+                </el-button>
+            </div>
+        </div>
 
-						$.ajax({
-							url: baseUrl + "/config/updateBatch",
-							headers: {
-								'token': localStorage.getItem('token')
-							},
-							data: JSON.stringify(updateCodeList),
-							dataType: 'json',
-							contentType: 'application/json;charset=UTF-8',
-							method: 'POST',
-							success: function(res) {
-								if (res.code == 200) {
-									that.$message({
-										message: '淇濆瓨鎴愬姛',
-										type: 'success'
-									});
-								} else if (res.code === 403) {
-									top.location.href = baseUrl + "/";
-								} else {
-									that.$message({
-										message: res.msg,
-										type: 'error'
-									});
-								}
-							}
-						});
-					},
-					getParamData(type) {
-						let data = []
-						if(type == "shuttle") {
-							data.push('dispatchShuttleMaxNum');
-							data.push('shuttleWrkInObligateCount');
-							data.push('avoidInnerCircle');
-							data.push('avoidOuterCircle');
-							data.push('direction_map');
-							data.push('shuttleDirectionReverse');
-							data.push('shuttleOutLiftLocationDistance');
-							data.push('shuttleMoveCommandsContinuously');
-							data.push('trafficControlRestartCalcPath');
-							data.push('wcsDebugShowLog');
-						}else if (type == "charge") {
-							data.push('chargeMaxValue');
-							data.push('shuttlePowerEarlyValue');
-							data.push('shuttleMaxPowerVerify');
-							data.push('timedCharge');
-							data.push('timedChargeRange');
-							data.push('shuttleDefaultChargePowerLine');
-							data.push('timedChargePowerLine');
-						}else if (type == "demo") {
-							data.push('demoRunLev');
-							data.push('demoSwitchLev');
-							data.push('demoCargoMove');
-							data.push('demoModeRunLoc');
-						}
+        <el-table :data="group.items" border stripe size="mini">
+            <el-table-column label="鍙傛暟" min-width="260">
+                <template slot-scope="scope">
+                    <div class="config-name">
+                        <span class="config-name-main">{{ scope.row.name || scope.row.code }}</span>
+                        <span class="config-name-sub">
+                            {{ getValueTypeText(scope.row) }}
+                            <span v-if="scope.row.status === 0"> / 宸茬鐢�</span>
+                        </span>
+                    </div>
+                </template>
+            </el-table-column>
 
-						return data;
-					},
-				},
-			})
-		</script>
-	</body>
+            <el-table-column label="缂栫爜" min-width="210">
+                <template slot-scope="scope">
+                    <div class="code-cell">{{ scope.row.code }}</div>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="褰撳墠鍊�" min-width="420">
+                <template slot-scope="scope">
+                    <div class="editor-cell">
+                        <el-radio-group v-if="resolveEditor(scope.row) === 'boolean'"
+                                        v-model="codeMap[scope.row.code]"
+                                        size="mini">
+                            <el-radio-button v-for="option in getBooleanOptions(scope.row)"
+                                             :key="option.value"
+                                             :label="option.value">
+                                {{ option.label }}
+                            </el-radio-button>
+                        </el-radio-group>
+
+                        <el-input v-else-if="resolveEditor(scope.row) === 'textarea'"
+                                  v-model="codeMap[scope.row.code]"
+                                  type="textarea"
+                                  resize="vertical"
+                                  :autosize="{ minRows: 2, maxRows: 5 }">
+                        </el-input>
+
+                        <el-input v-else
+                                  v-model="codeMap[scope.row.code]"
+                                  clearable>
+                        </el-input>
+                    </div>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="鐘舵��" width="170">
+                <template slot-scope="scope">
+                    <div class="status-cell">
+                        <el-tag v-if="isChanged(scope.row.code)" type="warning" size="mini">寰呬繚瀛�</el-tag>
+                        <el-tag v-else type="success" size="mini">宸插悓姝�</el-tag>
+                        <el-button v-if="isChanged(scope.row.code)"
+                                   type="text"
+                                   size="mini"
+                                   @click="resetConfig(scope.row.code)">
+                            杩樺師
+                        </el-button>
+                    </div>
+                </template>
+            </el-table-column>
+        </el-table>
+    </el-card>
+</div>
+
+<script>
+    new Vue({
+        el: '#app',
+        data: {
+            loading: false,
+            saveAllLoading: false,
+            saveGroupLoading: {},
+            configList: [],
+            deviceList: [],
+            codeMap: {},
+            originalCodeMap: {},
+            searchText: '',
+            selectedType: 'all',
+            changedOnly: false,
+            groupOrder: ['system', 'solver', 'crn', 'fake', 'notify', 'String', 'other']
+        },
+        computed: {
+            selectTypeOptions: function () {
+                var typeMap = {};
+                (this.configList || []).forEach(function (item) {
+                    var type = item && item.selectType ? item.selectType : 'other';
+                    typeMap[type] = true;
+                });
+                return this.sortTypes(Object.keys(typeMap));
+            },
+            deviceTypeCountMap: function () {
+                var result = {};
+                (this.deviceList || []).forEach(function (item) {
+                    if (!item || !item.deviceType) {
+                        return;
+                    }
+                    result[item.deviceType] = (result[item.deviceType] || 0) + 1;
+                });
+                return result;
+            },
+            fakeDeviceCount: function () {
+                var total = 0;
+                (this.deviceList || []).forEach(function (item) {
+                    if (item && String(item.fake) === '1') {
+                        total += 1;
+                    }
+                });
+                return total;
+            },
+            changedCount: function () {
+                var self = this;
+                var total = 0;
+                Object.keys(this.codeMap || {}).forEach(function (code) {
+                    if (self.isChanged(code)) {
+                        total += 1;
+                    }
+                });
+                return total;
+            },
+            hasChanges: function () {
+                return this.changedCount > 0;
+            },
+            summaryCards: function () {
+                return [
+                    {
+                        key: 'crn',
+                        label: '鍫嗗灈鏈�',
+                        value: this.deviceTypeCountMap.Crn || 0,
+                        hint: '褰撳墠宸查厤缃殑鍗曞伐浣嶅爢鍨涙満鏁伴噺'
+                    },
+                    {
+                        key: 'dualCrn',
+                        label: '鍙屽伐浣嶅爢鍨涙満',
+                        value: this.deviceTypeCountMap.DualCrn || 0,
+                        hint: '褰撳墠宸查厤缃殑鍙屽伐浣嶅爢鍨涙満鏁伴噺'
+                    },
+                    {
+                        key: 'devp',
+                        label: '杈撻�佺珯鎺у埗鍣�',
+                        value: this.deviceTypeCountMap.Devp || 0,
+                        hint: '褰撳墠绔欏彴/杈撻�佺嚎鎺у埗鍣ㄦ暟閲�'
+                    },
+                    {
+                        key: 'rgv',
+                        label: 'RGV',
+                        value: this.deviceTypeCountMap.Rgv || 0,
+                        hint: '褰撳墠 RGV 鎺у埗鍣ㄦ暟閲�'
+                    },
+                    {
+                        key: 'fake',
+                        label: '浠跨湡璁惧',
+                        value: this.fakeDeviceCount,
+                        hint: 'device_config 涓� fake=1 鐨勮澶囨暟'
+                    },
+                    {
+                        key: 'groups',
+                        label: '閰嶇疆鍒嗙粍',
+                        value: this.selectTypeOptions.length,
+                        hint: '鏉ヨ嚜 sys_config.select_type'
+                    },
+                    {
+                        key: 'configs',
+                        label: '鍙傛暟鎬绘暟',
+                        value: this.configList.length,
+                        hint: '褰撳墠椤甸潰绾冲叆绠$悊鐨� sys_config 鏁伴噺'
+                    },
+                    {
+                        key: 'changed',
+                        label: '寰呬繚瀛樺彉鏇�',
+                        value: this.changedCount,
+                        hint: this.changedCount > 0 ? '瀛樺湪灏氭湭鎻愪氦鐨勫弬鏁版敼鍔�' : '褰撳墠娌℃湁鏈繚瀛樼殑鏀瑰姩'
+                    }
+                ];
+            },
+            filteredGroups: function () {
+                var self = this;
+                var groups = {};
+                var keyword = (this.searchText || '').toLowerCase();
+                var list = Array.isArray(this.configList) ? this.configList.slice() : [];
+                list.sort(function (a, b) {
+                    return (a.id || 0) - (b.id || 0);
+                });
+
+                list.forEach(function (item) {
+                    if (!item || !item.code) {
+                        return;
+                    }
+                    var type = item.selectType || 'other';
+                    if (self.selectedType !== 'all' && self.selectedType !== type) {
+                        return;
+                    }
+                    if (keyword) {
+                        var text = ((item.name || '') + ' ' + item.code).toLowerCase();
+                        if (text.indexOf(keyword) === -1) {
+                            return;
+                        }
+                    }
+                    if (self.changedOnly && !self.isChanged(item.code)) {
+                        return;
+                    }
+                    if (!groups[type]) {
+                        groups[type] = {
+                            type: type,
+                            label: self.getGroupLabel(type),
+                            desc: self.getGroupDesc(type),
+                            deviceText: self.getGroupDeviceText(type),
+                            items: [],
+                            changedCount: 0
+                        };
+                    }
+                    groups[type].items.push(item);
+                    if (self.isChanged(item.code)) {
+                        groups[type].changedCount += 1;
+                    }
+                });
+
+                return self.sortTypes(Object.keys(groups)).map(function (type) {
+                    return groups[type];
+                });
+            }
+        },
+        created: function () {
+            this.reloadData();
+        },
+        methods: {
+            reloadData: function () {
+                var self = this;
+                var pending = 2;
+                var failed = false;
+                self.loading = true;
+
+                function finish() {
+                    pending -= 1;
+                    if (pending <= 0) {
+                        self.loading = false;
+                        if (failed) {
+                            self.$message.error('閮ㄥ垎鏁版嵁鍔犺浇澶辫触');
+                        }
+                    }
+                }
+
+                $.ajax({
+                    url: baseUrl + "/config/listAll/auth",
+                    headers: self.authHeaders(),
+                    dataType: 'json',
+                    method: 'GET',
+                    success: function (res) {
+                        if (self.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!res || res.code !== 200 || !Array.isArray(res.data)) {
+                            failed = true;
+                            finish();
+                            return;
+                        }
+                        self.configList = res.data.slice();
+                        self.rebuildCodeMaps();
+                        finish();
+                    },
+                    error: function () {
+                        failed = true;
+                        finish();
+                    }
+                });
+
+                $.ajax({
+                    url: baseUrl + "/deviceConfig/list/auth",
+                    headers: self.authHeaders(),
+                    dataType: 'json',
+                    method: 'GET',
+                    data: {
+                        curr: 1,
+                        limit: 200
+                    },
+                    success: function (res) {
+                        if (self.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!res || res.code !== 200 || !res.data) {
+                            failed = true;
+                            finish();
+                            return;
+                        }
+                        self.deviceList = Array.isArray(res.data.records) ? res.data.records : [];
+                        finish();
+                    },
+                    error: function () {
+                        failed = true;
+                        finish();
+                    }
+                });
+            },
+            rebuildCodeMaps: function () {
+                var current = {};
+                (this.configList || []).forEach(function (item) {
+                    if (item && item.code) {
+                        current[item.code] = item.value == null ? '' : String(item.value);
+                    }
+                });
+                this.codeMap = $.extend({}, current);
+                this.originalCodeMap = $.extend({}, current);
+            },
+            authHeaders: function () {
+                return {
+                    token: localStorage.getItem('token')
+                };
+            },
+            handleForbidden: function (res) {
+                if (res && res.code === 403) {
+                    top.location.href = baseUrl + "/";
+                    return true;
+                }
+                return false;
+            },
+            sortTypes: function (types) {
+                var self = this;
+                return (types || []).slice().sort(function (a, b) {
+                    var aIndex = self.groupOrder.indexOf(a);
+                    var bIndex = self.groupOrder.indexOf(b);
+                    if (aIndex === -1) {
+                        aIndex = self.groupOrder.length + 100;
+                    }
+                    if (bIndex === -1) {
+                        bIndex = self.groupOrder.length + 100;
+                    }
+                    if (aIndex !== bIndex) {
+                        return aIndex - bIndex;
+                    }
+                    return String(a).localeCompare(String(b));
+                });
+            },
+            getGroupLabel: function (type) {
+                var labels = {
+                    system: '绯荤粺杩愯鍙傛暟',
+                    solver: '姹傝В鍣ㄤ笌璋冨害鍙傛暟',
+                    crn: '鍫嗗灈鏈烘帶鍒跺弬鏁�',
+                    fake: '浠跨湡妯″紡鍙傛暟',
+                    notify: '閫氱煡涓婃姤鍙傛暟',
+                    String: '绔欑偣璺緞鍙傛暟',
+                    other: '鍏朵粬鍙傛暟'
+                };
+                return labels[type] || type;
+            },
+            getGroupDesc: function (type) {
+                var descMap = {
+                    system: '瑕嗙洊 WMS 鑱斿姩銆佽澶囨棩蹇椼�佽緭閫佺嚎浠诲姟鍜岀洃鎺у湴鍥剧瓑绯荤粺绾у弬鏁般��',
+                    solver: '鐢ㄤ簬鍫嗗灈鏈鸿皟搴︿笌姹傝В鍣ㄦ墽琛岋紝鍖呮嫭閫熷害銆佹潈閲嶃�佹渶澶ф眰瑙f椂闀跨瓑銆�',
+                    crn: '閽堝鍫嗗灈鏈烘帶鍒堕摼璺殑璋冭瘯鍙傛暟锛屼紭鍏堢敤浜庡紓甯稿洖婊氬拰鎵ц淇濇姢銆�',
+                    fake: '浠跨湡寮�鍏充笌浠诲姟鐢熸垚绛栫暐锛岄�傚悎鑱旇皟鎴栨紨绀虹幆澧冦��',
+                    notify: '澶栭儴娑堟伅閫氱煡銆侀拤閽夊憡璀﹀拰 WMS 鍥炶皟閲嶈瘯绛栫暐銆�',
+                    String: '绔欑偣璺緞璇勫垎涓庨粯璁ゆā鏉块�夋嫨锛屽奖鍝嶈緭閫佺珯鐐瑰寰勭粨鏋溿��',
+                    other: '褰撳墠鏈綊绫荤殑閰嶇疆椤广��'
+                };
+                return descMap[type] || '褰撳墠鍒嗙粍鏈厤缃鏄庛��';
+            },
+            getGroupDeviceText: function (type) {
+                var crnCount = this.deviceTypeCountMap.Crn || 0;
+                var dualCrnCount = this.deviceTypeCountMap.DualCrn || 0;
+                var devpCount = this.deviceTypeCountMap.Devp || 0;
+                var rgvCount = this.deviceTypeCountMap.Rgv || 0;
+                if (type === 'system') {
+                    return '璁惧鑼冨洿锛氬爢鍨涙満 ' + crnCount + ' 鍙� / 鍙屽伐浣� ' + dualCrnCount + ' 鍙� / 杈撻�佺珯鎺у埗鍣� ' + devpCount + ' 鍙� / RGV ' + rgvCount + ' 鍙�';
+                }
+                if (type === 'solver' || type === 'crn') {
+                    return '鍏宠仈鍫嗗灈鏈猴細鍗曞伐浣� ' + crnCount + ' 鍙� / 鍙屽伐浣� ' + dualCrnCount + ' 鍙�';
+                }
+                if (type === 'fake') {
+                    return '浠跨湡璁惧锛�' + this.fakeDeviceCount + ' 鍙�';
+                }
+                if (type === 'notify') {
+                    return '澶栭儴閾捐矾锛歐MS / 閽夐拤 / 閫氱煡閲嶈瘯';
+                }
+                if (type === 'String') {
+                    return '璺緞閰嶇疆锛氳緭閫佺珯鐐硅瘎鍒嗕笌妯℃澘';
+                }
+                return '';
+            },
+            getValueTypeText: function (config) {
+                if (config && config.type === 2) {
+                    return 'JSON';
+                }
+                if (this.resolveEditor(config) === 'boolean') {
+                    return '甯冨皵寮�鍏�';
+                }
+                if (this.resolveEditor(config) === 'textarea') {
+                    return '闀挎枃鏈�';
+                }
+                return '瀛楃涓�';
+            },
+            resolveEditor: function (config) {
+                var value = this.getCodeValue(config.code);
+                if (config && config.type === 2) {
+                    return 'textarea';
+                }
+                if (this.isBooleanValue(value)) {
+                    return 'boolean';
+                }
+                if ((config && /url|uri|path/i.test(config.code || '')) || String(value || '').length > 60) {
+                    return 'textarea';
+                }
+                return 'input';
+            },
+            isBooleanValue: function (value) {
+                var text = value == null ? '' : String(value);
+                return text === 'Y' || text === 'N' || text === 'true' || text === 'false';
+            },
+            getBooleanOptions: function (config) {
+                var value = this.getCodeValue(config.code);
+                if (String(value) === 'Y' || String(value) === 'N') {
+                    return [
+                        { label: '寮�', value: 'Y' },
+                        { label: '鍏�', value: 'N' }
+                    ];
+                }
+                return [
+                    { label: '寮�', value: 'true' },
+                    { label: '鍏�', value: 'false' }
+                ];
+            },
+            getCodeValue: function (code) {
+                if (!code) {
+                    return '';
+                }
+                return this.codeMap[code] == null ? '' : String(this.codeMap[code]);
+            },
+            isChanged: function (code) {
+                return this.getCodeValue(code) !== (this.originalCodeMap[code] == null ? '' : String(this.originalCodeMap[code]));
+            },
+            resetConfig: function (code) {
+                this.$set(this.codeMap, code, this.originalCodeMap[code] == null ? '' : String(this.originalCodeMap[code]));
+            },
+            collectChangedItems: function (items) {
+                var self = this;
+                return (items || []).filter(function (item) {
+                    return item && item.code && self.isChanged(item.code);
+                });
+            },
+            validateItems: function (items) {
+                var error = '';
+                (items || []).some(function (item) {
+                    if (!item) {
+                        return false;
+                    }
+                    if (item.type === 2) {
+                        try {
+                            JSON.parse(item.value);
+                        } catch (e) {
+                            error = (item.name || item.code) + ' JSON 鏍煎紡涓嶆纭�';
+                            return true;
+                        }
+                    }
+                    return false;
+                });
+                return error;
+            },
+            doSave: function (items, saveKey) {
+                var self = this;
+                var changedItems = self.collectChangedItems(items);
+                if (changedItems.length === 0) {
+                    self.$message({
+                        message: '褰撳墠娌℃湁闇�瑕佷繚瀛樼殑鍙樻洿',
+                        type: 'info'
+                    });
+                    return;
+                }
+
+                var payload = changedItems.map(function (item) {
+                    return {
+                        id: item.id,
+                        name: item.name,
+                        code: item.code,
+                        value: self.getCodeValue(item.code),
+                        type: item.type,
+                        status: item.status,
+                        selectType: item.selectType
+                    };
+                });
+
+                var validateMsg = self.validateItems(payload);
+                if (validateMsg) {
+                    self.$message({
+                        message: validateMsg,
+                        type: 'error'
+                    });
+                    return;
+                }
+
+                if (saveKey === 'all') {
+                    self.saveAllLoading = true;
+                } else {
+                    self.$set(self.saveGroupLoading, saveKey, true);
+                }
+
+                $.ajax({
+                    url: baseUrl + "/config/updateBatch",
+                    headers: self.authHeaders(),
+                    data: JSON.stringify(payload.map(function (item) {
+                        return {
+                            code: item.code,
+                            value: item.value
+                        };
+                    })),
+                    dataType: 'json',
+                    contentType: 'application/json;charset=UTF-8',
+                    method: 'POST',
+                    success: function (res) {
+                        if (saveKey === 'all') {
+                            self.saveAllLoading = false;
+                        } else {
+                            self.$set(self.saveGroupLoading, saveKey, false);
+                        }
+                        if (self.handleForbidden(res)) {
+                            return;
+                        }
+                        if (!res || res.code !== 200) {
+                            self.$message({
+                                message: res && res.msg ? res.msg : '淇濆瓨澶辫触',
+                                type: 'error'
+                            });
+                            return;
+                        }
+                        payload.forEach(function (item) {
+                            self.originalCodeMap[item.code] = item.value == null ? '' : String(item.value);
+                        });
+                        self.$message({
+                            message: '淇濆瓨鎴愬姛锛岃繍琛岀紦瀛樺凡鍚屾鍒锋柊',
+                            type: 'success'
+                        });
+                    },
+                    error: function () {
+                        if (saveKey === 'all') {
+                            self.saveAllLoading = false;
+                        } else {
+                            self.$set(self.saveGroupLoading, saveKey, false);
+                        }
+                        self.$message({
+                            message: '淇濆瓨澶辫触',
+                            type: 'error'
+                        });
+                    }
+                });
+            },
+            saveGroup: function (group) {
+                this.doSave(group.items, group.type);
+            },
+            saveAll: function () {
+                this.doSave(this.configList, 'all');
+            }
+        }
+    });
+</script>
+</body>
 
 </html>

--
Gitblit v1.9.1