From b37e141c00a123cf362fae00c1e63175d41c4bbe Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 10 三月 2026 17:01:06 +0800
Subject: [PATCH] #
---
src/main/webapp/views/ai/llm_config.html | 189 +++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 183 insertions(+), 6 deletions(-)
diff --git a/src/main/webapp/views/ai/llm_config.html b/src/main/webapp/views/ai/llm_config.html
index e5c052e..d6e2849 100644
--- a/src/main/webapp/views/ai/llm_config.html
+++ b/src/main/webapp/views/ai/llm_config.html
@@ -47,6 +47,13 @@
font-size: 12px;
opacity: 0.9;
}
+ .hero-actions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ }
.summary-grid {
margin-top: 10px;
display: grid;
@@ -260,8 +267,10 @@
<div class="sub">鏀寔澶欰PI銆佸妯″瀷銆佸Key锛岄搴﹁�楀敖鎴栨晠闅滆嚜鍔ㄥ垏鎹�</div>
</div>
</div>
- <div>
+ <div class="hero-actions">
<el-button type="primary" size="mini" @click="addRoute">鏂板璺敱</el-button>
+ <el-button size="mini" @click="exportRoutes">瀵煎嚭JSON</el-button>
+ <el-button size="mini" @click="triggerImport">瀵煎叆JSON</el-button>
<el-button size="mini" @click="loadRoutes">鍒锋柊</el-button>
<el-button size="mini" @click="openLogDialog">璋冪敤鏃ュ織</el-button>
</div>
@@ -289,6 +298,7 @@
</div>
</div>
</div>
+ <input ref="importFileInput" type="file" accept="application/json,.json" style="display:none;" @change="handleImportFileChange" />
<div class="route-board" v-loading="loading">
<div v-if="!routes || routes.length === 0" class="empty-shell">
@@ -299,7 +309,11 @@
<div class="route-card" :class="routeCardClass(route)" v-for="(route, idx) in routes" :key="route.id ? ('route_' + route.id) : ('new_' + idx)">
<div class="route-head">
<div class="route-title">
- <el-input v-model="route.name" size="mini" placeholder="璺敱鍚嶇О"></el-input>
+ <el-input
+ :value="displayRouteName(route)"
+ size="mini"
+ placeholder="璺敱鍚嶇О"
+ @input="handleRouteNameInput(route, $event)"></el-input>
<div class="route-id-line">#{{ route.id || 'new' }} 路 浼樺厛绾� {{ route.priority || 0 }}</div>
</div>
<div class="route-state">
@@ -383,7 +397,7 @@
</div>
</div>
- <el-dialog title="LLM璋冪敤鏃ュ織" :visible.sync="logDialogVisible" width="88%" :close-on-click-modal="false">
+ <el-dialog :title="i18n('llm.logsTitle', 'LLM璋冪敤鏃ュ織')" :visible.sync="logDialogVisible" width="88%" :close-on-click-modal="false">
<div class="log-toolbar">
<el-select v-model="logQuery.scene" size="mini" clearable placeholder="鍦烘櫙" style="width:180px;">
<el-option label="chat" value="chat"></el-option>
@@ -447,7 +461,7 @@
</div>
</el-dialog>
- <el-dialog :title="logDetailTitle || '鏃ュ織璇︽儏'" :visible.sync="logDetailVisible" width="82%" :close-on-click-modal="false" append-to-body>
+ <el-dialog :title="logDetailTitle || i18n('llm.logDetailTitle', '鏃ュ織璇︽儏')" :visible.sync="logDetailVisible" width="82%" :close-on-click-modal="false" append-to-body>
<div class="log-detail-body">{{ logDetailText || '-' }}</div>
<span slot="footer" class="dialog-footer">
<el-button size="mini" @click="copyText(logDetailText)">澶嶅埗鍏ㄦ枃</el-button>
@@ -458,7 +472,7 @@
<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/common.js" charset="utf-8"></script>
+<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
<script>
new Vue({
el: '#app',
@@ -501,6 +515,31 @@
}
},
methods: {
+ i18n: function(key, fallback, params) {
+ if (window.WCS_I18N && typeof window.WCS_I18N.t === 'function') {
+ var translated = window.WCS_I18N.t(key, params);
+ if (translated && translated !== key) {
+ return translated;
+ }
+ }
+ return fallback || key;
+ },
+ translateLegacyText: function(text) {
+ if (window.WCS_I18N && typeof window.WCS_I18N.tl === 'function') {
+ return window.WCS_I18N.tl(text);
+ }
+ return text;
+ },
+ displayRouteName: function(route) {
+ var value = route && route.name ? String(route.name) : '';
+ return this.translateLegacyText(value);
+ },
+ handleRouteNameInput: function(route, value) {
+ if (!route) {
+ return;
+ }
+ route.name = value;
+ },
formatDateTime: function(input) {
if (!input) return '-';
var d = input instanceof Date ? input : new Date(input);
@@ -597,6 +636,138 @@
authHeaders: function() {
return { 'token': localStorage.getItem('token') };
},
+ exportRoutes: function() {
+ var self = this;
+ fetch(baseUrl + '/ai/llm/config/export/auth', { headers: self.authHeaders() })
+ .then(function(r){ return r.json(); })
+ .then(function(res){
+ if (!res || res.code !== 200) {
+ self.$message.error((res && res.msg) ? res.msg : '瀵煎嚭澶辫触');
+ return;
+ }
+ var payload = res.data || {};
+ var text = JSON.stringify(payload, null, 2);
+ var name = 'llm_routes_' + self.buildExportTimestamp() + '.json';
+ var blob = new Blob([text], { type: 'application/json;charset=utf-8' });
+ var a = document.createElement('a');
+ a.href = URL.createObjectURL(blob);
+ a.download = name;
+ document.body.appendChild(a);
+ a.click();
+ setTimeout(function() {
+ URL.revokeObjectURL(a.href);
+ document.body.removeChild(a);
+ }, 0);
+ self.$message.success('瀵煎嚭鎴愬姛');
+ })
+ .catch(function(){
+ self.$message.error('瀵煎嚭澶辫触');
+ });
+ },
+ buildExportTimestamp: function() {
+ var d = new Date();
+ var pad = function(n) { return n < 10 ? ('0' + n) : String(n); };
+ return d.getFullYear()
+ + pad(d.getMonth() + 1)
+ + pad(d.getDate())
+ + '_'
+ + pad(d.getHours())
+ + pad(d.getMinutes())
+ + pad(d.getSeconds());
+ },
+ triggerImport: function() {
+ var input = this.$refs.importFileInput;
+ if (!input) return;
+ input.value = '';
+ input.click();
+ },
+ handleImportFileChange: function(evt) {
+ var self = this;
+ var files = evt && evt.target && evt.target.files ? evt.target.files : null;
+ var file = files && files.length > 0 ? files[0] : null;
+ if (!file) return;
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ var text = e && e.target ? e.target.result : '';
+ var parsed;
+ try {
+ parsed = JSON.parse(text || '{}');
+ } catch (err) {
+ self.$message.error('JSON 鏍煎紡涓嶆纭�');
+ return;
+ }
+ var routes = self.extractImportRoutes(parsed);
+ if (!routes || routes.length === 0) {
+ self.$message.warning('鏈壘鍒板彲瀵煎叆鐨� routes');
+ return;
+ }
+ self.$confirm(
+ '璇烽�夋嫨瀵煎叆鏂瑰紡锛氳鐩栧鍏ヤ細鍏堟竻绌虹幇鏈夎矾鐢憋紱鐐瑰嚮鈥滃悎骞跺鍏モ�濆垯鎸塈D鏇存柊鎴栨柊澧炪��',
+ '瀵煎叆纭',
+ {
+ type: 'warning',
+ distinguishCancelAndClose: true,
+ confirmButtonText: '瑕嗙洊瀵煎叆',
+ cancelButtonText: '鍚堝苟瀵煎叆',
+ closeOnClickModal: false
+ }
+ ).then(function() {
+ self.doImportRoutes(routes, true);
+ }).catch(function(action) {
+ if (action === 'cancel') {
+ self.doImportRoutes(routes, false);
+ }
+ });
+ };
+ reader.onerror = function() {
+ self.$message.error('璇诲彇鏂囦欢澶辫触');
+ };
+ reader.readAsText(file, 'utf-8');
+ },
+ extractImportRoutes: function(parsed) {
+ if (Array.isArray(parsed)) return parsed;
+ if (!parsed || typeof parsed !== 'object') return [];
+ if (Array.isArray(parsed.routes)) return parsed.routes;
+ if (parsed.data && Array.isArray(parsed.data.routes)) return parsed.data.routes;
+ if (Array.isArray(parsed.data)) return parsed.data;
+ return [];
+ },
+ doImportRoutes: function(routes, replace) {
+ var self = this;
+ fetch(baseUrl + '/ai/llm/config/import/auth', {
+ method: 'POST',
+ headers: Object.assign({ 'Content-Type': 'application/json' }, self.authHeaders()),
+ body: JSON.stringify({
+ replace: replace === true,
+ routes: routes
+ })
+ })
+ .then(function(r){ return r.json(); })
+ .then(function(res){
+ if (!res || res.code !== 200) {
+ self.$message.error((res && res.msg) ? res.msg : '瀵煎叆澶辫触');
+ return;
+ }
+ var d = res.data || {};
+ var msg = '瀵煎叆瀹屾垚锛氭柊澧� ' + (d.inserted || 0)
+ + '锛屾洿鏂� ' + (d.updated || 0)
+ + '锛岃烦杩� ' + (d.skipped || 0);
+ if (d.errorCount && d.errorCount > 0) {
+ msg += '锛屽紓甯� ' + d.errorCount;
+ }
+ self.$message.success(msg);
+ if (Array.isArray(d.errors) && d.errors.length > 0) {
+ self.$alert(d.errors.join('\n'), '瀵煎叆寮傚父鏄庣粏锛堟渶澶�20鏉★級', {
+ confirmButtonText: '纭畾',
+ type: 'warning'
+ });
+ }
+ self.loadRoutes();
+ })
+ .catch(function(){
+ self.$message.error('瀵煎叆澶辫触');
+ });
+ },
handleRouteCommand: function(command, route, idx) {
if (command === 'test') return this.testRoute(route);
if (command === 'save') return this.saveRoute(route);
@@ -659,7 +830,7 @@
+ '閿欒: ' + (row.errorMessage || '-') + '\n\n'
+ '璇锋眰:\n' + (row.requestContent || '-') + '\n\n'
+ '鍝嶅簲:\n' + (row.responseContent || '-');
- this.logDetailTitle = '鏃ュ織璇︽儏 - ' + (row.traceId || row.id || '');
+ this.logDetailTitle = this.i18n('llm.logDetailPrefix', '鏃ュ織璇︽儏 - ') + (row.traceId || row.id || '');
this.logDetailText = text;
this.logDetailVisible = true;
},
@@ -864,6 +1035,12 @@
}
},
mounted: function() {
+ var self = this;
+ if (window.WCS_I18N && typeof window.WCS_I18N.onReady === 'function') {
+ window.WCS_I18N.onReady(function() {
+ self.$forceUpdate();
+ });
+ }
this.loadRoutes();
}
});
--
Gitblit v1.9.1