@hina114514/chaite 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-DXFlhLNW.mjs → adapters-CKtCN3uS.mjs} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-9THlMdwY.mjs → src-Cf1BYmcj.mjs} +3 -3
- package/dist/{types-Dxb7JkMC.mjs → types-BGIMNqTC.mjs} +1 -1
- package/frontend/build/index.html +353 -344
- package/package.json +1 -1
|
@@ -3,123 +3,147 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8"/>
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
6
|
-
<title>Chaite
|
|
6
|
+
<title>Chaite · 光影解码</title>
|
|
7
7
|
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
|
8
8
|
<style>
|
|
9
9
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
10
10
|
|
|
11
11
|
:root{
|
|
12
|
-
--c-bg:#
|
|
13
|
-
--c-surface:rgba(
|
|
14
|
-
--c-surface2:rgba(24,
|
|
15
|
-
--c-surface3:rgba(
|
|
16
|
-
--c-border:rgba(255,
|
|
17
|
-
--c-border2:rgba(255,
|
|
18
|
-
--c-text:#
|
|
19
|
-
--c-dim:#
|
|
20
|
-
--c-faint:#
|
|
21
|
-
--c-accent:#
|
|
22
|
-
--c-
|
|
23
|
-
--c-
|
|
24
|
-
--c-
|
|
25
|
-
--c-
|
|
26
|
-
--c-
|
|
27
|
-
--c-
|
|
28
|
-
--c-
|
|
29
|
-
--c-
|
|
12
|
+
--c-bg:#0d0b14;
|
|
13
|
+
--c-surface:rgba(24,18,36,0.78);
|
|
14
|
+
--c-surface2:rgba(32,24,48,0.88);
|
|
15
|
+
--c-surface3:rgba(44,36,62,0.68);
|
|
16
|
+
--c-border:rgba(255,183,197,0.08);
|
|
17
|
+
--c-border2:rgba(255,183,197,0.15);
|
|
18
|
+
--c-text:#eeeaf4;
|
|
19
|
+
--c-dim:#a89cb8;
|
|
20
|
+
--c-faint:#6e6480;
|
|
21
|
+
--c-accent:#ff9aa2;
|
|
22
|
+
--c-accent2:rgba(255,154,162,0.14);
|
|
23
|
+
--c-accent-glow:rgba(255,154,162,0.25);
|
|
24
|
+
--c-pink:#ffb7c5;
|
|
25
|
+
--c-pink2:rgba(255,183,197,0.1);
|
|
26
|
+
--c-green:#8fd19e;
|
|
27
|
+
--c-green2:rgba(143,209,158,0.12);
|
|
28
|
+
--c-red:#ff8589;
|
|
29
|
+
--c-red2:rgba(255,133,137,0.12);
|
|
30
|
+
--c-yellow:#ffe0a0;
|
|
31
|
+
--c-purple:#c9b1ff;
|
|
30
32
|
--r:10px;
|
|
31
33
|
--r2:16px;
|
|
32
|
-
--ease:
|
|
33
|
-
--ease2: cubic-bezier(.7,0,.3,1);
|
|
34
|
+
--ease:cubic-bezier(.16,1,.3,1);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
body{
|
|
37
38
|
background:var(--c-bg);
|
|
38
39
|
color:var(--c-text);
|
|
39
|
-
font-family: '
|
|
40
|
-
font-size:14px;
|
|
41
|
-
line-height:1.6;
|
|
42
|
-
min-height:100vh;
|
|
43
|
-
overflow-x:hidden;
|
|
40
|
+
font-family:'PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif;
|
|
41
|
+
font-size:14px;line-height:1.6;min-height:100vh;overflow-x:hidden;
|
|
44
42
|
-webkit-font-smoothing:antialiased;
|
|
45
43
|
}
|
|
46
44
|
body::before{
|
|
47
|
-
content:'';position:fixed;inset:0;z-index:-1;
|
|
45
|
+
content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;
|
|
48
46
|
background:
|
|
49
|
-
radial-gradient(ellipse
|
|
50
|
-
radial-gradient(ellipse 50% 40% at
|
|
51
|
-
radial-gradient(ellipse
|
|
52
|
-
pointer-events:none;
|
|
47
|
+
radial-gradient(ellipse 70% 50% at 0% 0%,rgba(255,154,162,.1),transparent 70%),
|
|
48
|
+
radial-gradient(ellipse 50% 40% at 100% 20%,rgba(255,183,197,.06),transparent 60%),
|
|
49
|
+
radial-gradient(ellipse 50% 50% at 50% 90%,rgba(201,177,255,.05),transparent 60%);
|
|
53
50
|
}
|
|
54
51
|
|
|
52
|
+
/* Sakura petals (CSS only) */
|
|
53
|
+
@keyframes fall{0%{transform:translateY(-10vh) rotate(0deg);opacity:1}100%{transform:translateY(110vh) rotate(720deg);opacity:0}}
|
|
54
|
+
@keyframes sway{0%,100%{margin-left:0}50%{margin-left:30px}}
|
|
55
|
+
.petal{position:fixed;top:-20px;z-index:-1;pointer-events:none;font-size:16px;opacity:.3;animation:fall linear infinite,sway 4s ease-in-out infinite}
|
|
56
|
+
.petal:nth-child(1){left:5%;animation-duration:12s,4s;animation-delay:0s;font-size:14px}
|
|
57
|
+
.petal:nth-child(2){left:15%;animation-duration:10s,3.5s;animation-delay:2s;font-size:18px}
|
|
58
|
+
.petal:nth-child(3){left:25%;animation-duration:14s,4.5s;animation-delay:4s;font-size:12px}
|
|
59
|
+
.petal:nth-child(4){left:40%;animation-duration:11s,3.8s;animation-delay:1s;font-size:20px}
|
|
60
|
+
.petal:nth-child(5){left:55%;animation-duration:13s,4.2s;animation-delay:3s;font-size:15px}
|
|
61
|
+
.petal:nth-child(6){left:65%;animation-duration:9s,3.2s;animation-delay:5s;font-size:17px}
|
|
62
|
+
.petal:nth-child(7){left:75%;animation-duration:15s,4.8s;animation-delay:0s;font-size:13px}
|
|
63
|
+
.petal:nth-child(8){left:85%;animation-duration:10.5s,3.6s;animation-delay:3.5s;font-size:16px}
|
|
64
|
+
.petal:nth-child(9){left:92%;animation-duration:12.5s,4s;animation-delay:1.5s;font-size:14px}
|
|
65
|
+
.petal:nth-child(10){left:50%;animation-duration:11.5s,3.9s;animation-delay:6s;font-size:19px}
|
|
66
|
+
|
|
55
67
|
#app{min-height:100vh}
|
|
56
68
|
|
|
57
69
|
/* Login */
|
|
58
70
|
.login-wrap{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:20px}
|
|
59
71
|
.login-card{
|
|
60
72
|
width:100%;max-width:420px;background:var(--c-surface);border:1px solid var(--c-border);
|
|
61
|
-
border-radius:
|
|
62
|
-
backdrop-filter:blur(24px) saturate(1.4)
|
|
63
|
-
box-shadow:0 24px 80px rgba(0,0,0,.
|
|
73
|
+
border-radius:20px;padding:52px 40px;
|
|
74
|
+
backdrop-filter:blur(24px) saturate(1.4);
|
|
75
|
+
box-shadow:0 24px 80px rgba(0,0,0,.5),inset 0 1px 0 rgba(255,183,197,.06);
|
|
64
76
|
text-align:center;animation:fadeUp .5s var(--ease) both
|
|
65
77
|
}
|
|
66
|
-
.login-card h1{
|
|
78
|
+
.login-card h1{
|
|
79
|
+
font-size:32px;font-weight:700;letter-spacing:-.5px;margin-bottom:4px;
|
|
80
|
+
background:linear-gradient(135deg,#ffb7c5,#ff9aa2,#ffb7b2,#c9b1ff);
|
|
81
|
+
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-size:200% auto;
|
|
82
|
+
animation:logoShine 3s linear infinite
|
|
83
|
+
}
|
|
84
|
+
@keyframes logoShine{0%{background-position:0% center}100%{background-position:200% center}}
|
|
67
85
|
.login-card .sub{color:var(--c-dim);font-size:13px;margin-bottom:32px;letter-spacing:.2px}
|
|
68
|
-
.login-card input{width:100%;padding:12px 16px;border-radius:10px;border:1px solid var(--c-border);background:rgba(255,255,255,.03);color:var(--c-text);font-size:14px;outline:none;transition:all .2s
|
|
86
|
+
.login-card input{width:100%;padding:12px 16px;border-radius:10px;border:1px solid var(--c-border);background:rgba(255,255,255,.03);color:var(--c-text);font-size:14px;outline:none;transition:all .2s}
|
|
69
87
|
.login-card input:focus{border-color:var(--c-accent);box-shadow:0 0 0 3px var(--c-accent-glow)}
|
|
70
88
|
.login-card .err{color:var(--c-red);font-size:13px;margin:10px 0;min-height:20px}
|
|
71
89
|
.login-card .hint{font-size:11px;color:var(--c-faint);margin-top:16px}
|
|
72
|
-
.login-card .hint code{background:var(--c-surface2);padding:2px 8px;border-radius:4px;font-size:11px}
|
|
90
|
+
.login-card .hint code{background:var(--c-surface2);padding:2px 8px;border-radius:4px;font-size:11px;color:var(--c-pink)}
|
|
73
91
|
|
|
74
92
|
/* Buttons */
|
|
75
|
-
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:8px 16px;border-radius:
|
|
93
|
+
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:8px 16px;border-radius:10px;font-size:13px;font-weight:500;border:1px solid var(--c-border);color:var(--c-text);background:var(--c-surface2);cursor:pointer;transition:all .2s var(--ease);white-space:nowrap;letter-spacing:.2px}
|
|
76
94
|
.btn:hover{background:var(--c-surface3);border-color:var(--c-border2);transform:translateY(-1px)}
|
|
77
|
-
.btn:active{transform:translateY(0) scale(.
|
|
95
|
+
.btn:active{transform:translateY(0) scale(.97)}
|
|
78
96
|
.btn:disabled{opacity:.35;pointer-events:none}
|
|
79
|
-
.btn.pri{background:linear-gradient(135deg,#
|
|
80
|
-
.btn.pri:hover{box-shadow:0 4px 24px rgba(
|
|
81
|
-
.btn.danger{color:var(--c-red);border-color:rgba(
|
|
97
|
+
.btn.pri{background:linear-gradient(135deg,#ff9aa2,#ff8589);border-color:transparent;color:#fff;box-shadow:0 2px 14px var(--c-accent-glow)}
|
|
98
|
+
.btn.pri:hover{box-shadow:0 4px 24px rgba(255,154,162,.4);background:linear-gradient(135deg,#ffb7c5,#ff9aa2)}
|
|
99
|
+
.btn.danger{color:var(--c-red);border-color:rgba(255,133,137,.2)}
|
|
82
100
|
.btn.danger:hover{background:var(--c-red2)}
|
|
83
|
-
.btn.sm{padding:5px
|
|
101
|
+
.btn.sm{padding:5px 12px;font-size:11.5px;border-radius:7px}
|
|
84
102
|
.btn.ghost{background:transparent;border-color:transparent;color:var(--c-dim)}
|
|
85
103
|
.btn.ghost:hover{color:var(--c-text);background:rgba(255,255,255,.03)}
|
|
86
104
|
.btn .spin{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,.2);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite}
|
|
105
|
+
.btn .icon-s{margin-right:2px}
|
|
87
106
|
|
|
88
107
|
/* Layout */
|
|
89
108
|
.layout{display:flex;min-height:100vh}
|
|
90
|
-
.sidebar{width:
|
|
91
|
-
.sidebar-logo{padding:
|
|
92
|
-
.sidebar-logo h2{
|
|
109
|
+
.sidebar{width:230px;min-width:230px;background:rgba(16,12,24,0.82);border-right:1px solid var(--c-border);display:flex;flex-direction:column;backdrop-filter:blur(20px);box-shadow:2px 0 40px rgba(0,0,0,.35);transition:transform .35s var(--ease);z-index:50}
|
|
110
|
+
.sidebar-logo{padding:26px 20px 18px;border-bottom:1px solid var(--c-border)}
|
|
111
|
+
.sidebar-logo h2{
|
|
112
|
+
font-size:22px;font-weight:700;letter-spacing:-.5px;
|
|
113
|
+
background:linear-gradient(135deg,#ffb7c5,#ff9aa2,#ffb7b2);-webkit-background-clip:text;-webkit-text-fill-color:transparent
|
|
114
|
+
}
|
|
93
115
|
.sidebar-logo .ver{font-size:11px;color:var(--c-faint);margin-top:2px}
|
|
94
|
-
.sidebar-nav{flex:1;padding:
|
|
95
|
-
.nav-item{display:flex;align-items:center;gap:10px;padding:
|
|
116
|
+
.sidebar-nav{flex:1;padding:10px 10px}
|
|
117
|
+
.nav-item{display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:10px;font-size:13.5px;color:var(--c-dim);cursor:pointer;transition:all .15s var(--ease);margin-bottom:2px;position:relative}
|
|
96
118
|
.nav-item:hover{color:var(--c-text);background:var(--c-surface2)}
|
|
97
119
|
.nav-item.active{color:var(--c-accent);background:var(--c-accent2);font-weight:500}
|
|
98
|
-
.nav-item
|
|
120
|
+
.nav-item.active::after{content:'';position:absolute;left:-10px;top:50%;transform:translateY(-50%);width:3px;height:16px;border-radius:2px;background:var(--c-accent)}
|
|
121
|
+
.nav-item .icon{width:18px;text-align:center;font-size:15px;line-height:1;opacity:.8}
|
|
99
122
|
.nav-item.active .icon{opacity:1}
|
|
100
123
|
.nav-item .badge-mini{margin-left:auto;font-size:10px;padding:1px 7px;border-radius:8px;background:var(--c-accent2);color:var(--c-accent);font-weight:500}
|
|
101
124
|
.sidebar-footer{padding:16px 20px;border-top:1px solid var(--c-border);font-size:11px;color:var(--c-faint)}
|
|
102
|
-
.sidebar-footer .dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:
|
|
125
|
+
.sidebar-footer .dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:8px}
|
|
103
126
|
.sidebar-footer .dot.ok{background:var(--c-green);box-shadow:0 0 8px var(--c-green);animation:pulse-dot 2s infinite}
|
|
104
127
|
|
|
105
128
|
.main{flex:1;overflow-x:hidden;display:flex;flex-direction:column}
|
|
106
|
-
.topbar{padding:16px 28px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--c-border);gap:12px;backdrop-filter:blur(20px)
|
|
107
|
-
.topbar .t{font-size:15px;font-weight:600;color:var(--c-text);letter-spacing:-.3px}
|
|
129
|
+
.topbar{padding:16px 28px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--c-border);gap:12px;backdrop-filter:blur(20px);position:sticky;top:0;z-index:40;background:rgba(13,11,20,.85)}
|
|
130
|
+
.topbar .t{font-size:15px;font-weight:600;color:var(--c-text);letter-spacing:-.3px;display:flex;align-items:center;gap:8px}
|
|
131
|
+
.topbar .t::before{content:'🌸';font-size:13px}
|
|
108
132
|
.topbar .meta{display:flex;align-items:center;gap:10px}
|
|
109
133
|
.topbar .refresh-text{font-size:11px;color:var(--c-faint)}
|
|
110
134
|
.content{padding:28px 28px 48px;flex:1}
|
|
111
135
|
|
|
112
136
|
/* Toast */
|
|
113
|
-
.toast{position:fixed;top:24px;right:24px;z-index:200;padding:11px 20px;border-radius:10px;font-size:13px;font-weight:500;animation:slideRight .35s var(--ease) both;backdrop-filter:blur(16px)
|
|
114
|
-
.toast.ok{background:rgba(
|
|
115
|
-
.toast.err{background:rgba(
|
|
137
|
+
.toast{position:fixed;top:24px;right:24px;z-index:200;padding:11px 20px;border-radius:10px;font-size:13px;font-weight:500;animation:slideRight .35s var(--ease) both;backdrop-filter:blur(16px);box-shadow:0 8px 32px rgba(0,0,0,.4)}
|
|
138
|
+
.toast.ok{background:rgba(143,209,158,.92);color:#0a2816}
|
|
139
|
+
.toast.err{background:rgba(255,133,137,.92);color:#fff}
|
|
116
140
|
|
|
117
141
|
.menu-toggle{display:none;width:36px;height:36px;align-items:center;justify-content:center;border:none;background:none;color:var(--c-text);cursor:pointer;font-size:20px}
|
|
118
|
-
.sidebar-overlay{display:none;position:fixed;inset:0;z-index:45;background:rgba(0,0,0,.5);backdrop-filter:blur(4px)
|
|
142
|
+
.sidebar-overlay{display:none;position:fixed;inset:0;z-index:45;background:rgba(0,0,0,.5);backdrop-filter:blur(4px)}
|
|
119
143
|
|
|
120
144
|
/* Cards */
|
|
121
|
-
.card{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r2);padding:24px;margin-bottom:18px;backdrop-filter:blur(20px)
|
|
122
|
-
.card:hover{border-color:
|
|
145
|
+
.card{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r2);padding:24px;margin-bottom:18px;backdrop-filter:blur(20px);box-shadow:0 4px 24px rgba(0,0,0,.25),inset 0 1px 0 rgba(255,183,197,.04);animation:fadeUp .45s var(--ease) both;transition:border-color .3s}
|
|
146
|
+
.card:hover{border-color:var(--c-border2)}
|
|
123
147
|
.card:nth-child(1){animation-delay:0s}
|
|
124
148
|
.card:nth-child(2){animation-delay:.06s}
|
|
125
149
|
.card:nth-child(3){animation-delay:.12s}
|
|
@@ -131,8 +155,8 @@ body::before{
|
|
|
131
155
|
|
|
132
156
|
/* Stats */
|
|
133
157
|
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:14px;margin-bottom:22px}
|
|
134
|
-
.stat{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r2);padding:20px 22px;text-align:center;backdrop-filter:blur(20px)
|
|
135
|
-
.stat:hover{transform:translateY(-3px);border-color:var(--c-accent);box-shadow:0 8px 28px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,
|
|
158
|
+
.stat{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--r2);padding:20px 22px;text-align:center;backdrop-filter:blur(20px);box-shadow:0 2px 16px rgba(0,0,0,.2),inset 0 1px 0 rgba(255,183,197,.03);transition:all .25s var(--ease);cursor:default;animation:fadeUp .4s var(--ease) both}
|
|
159
|
+
.stat:hover{transform:translateY(-3px);border-color:var(--c-accent);box-shadow:0 8px 28px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,183,197,.08)}
|
|
136
160
|
.stat:nth-child(1){animation-delay:0s}
|
|
137
161
|
.stat:nth-child(2){animation-delay:.05s}
|
|
138
162
|
.stat:nth-child(3){animation-delay:.1s}
|
|
@@ -148,71 +172,80 @@ body::before{
|
|
|
148
172
|
.tbl th{text-align:left;padding:10px 14px;color:var(--c-faint);font-weight:500;border-bottom:1px solid var(--c-border);font-size:11px;text-transform:uppercase;letter-spacing:.6px}
|
|
149
173
|
.tbl td{padding:10px 14px;border-bottom:1px solid var(--c-border)}
|
|
150
174
|
.tbl tbody tr{transition:background .15s}
|
|
151
|
-
.tbl tbody tr:hover{background:rgba(255,
|
|
175
|
+
.tbl tbody tr:hover{background:rgba(255,183,197,.03)}
|
|
152
176
|
|
|
153
177
|
/* Pills */
|
|
154
178
|
.pill{display:inline-block;padding:2px 10px;border-radius:10px;font-size:11px;font-weight:500;letter-spacing:.2px}
|
|
155
179
|
.pill.on{background:var(--c-green2);color:var(--c-green)}
|
|
156
180
|
.pill.off{background:var(--c-red2);color:var(--c-red)}
|
|
157
|
-
.pill.model{background:var(--c-accent2);color:var(--c-
|
|
158
|
-
.pill.tag{background:rgba(
|
|
181
|
+
.pill.model{background:var(--c-accent2);color:var(--c-pink);margin:2px 3px 2px 0}
|
|
182
|
+
.pill.tag{background:rgba(201,177,255,.12);color:var(--c-purple);margin:2px 3px 2px 0}
|
|
159
183
|
|
|
160
184
|
/* Config grid cards */
|
|
161
|
-
.cfg-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(
|
|
162
|
-
.cfg-card{background:rgba(255,
|
|
163
|
-
.cfg-card:hover{border-color:
|
|
164
|
-
.cfg-card-head{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;background:rgba(255,
|
|
165
|
-
.cfg-card-head:hover{background:rgba(255,
|
|
185
|
+
.cfg-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:14px;margin-top:16px}
|
|
186
|
+
.cfg-card{background:rgba(255,183,197,.02);border:1px solid var(--c-border);border-radius:14px;overflow:hidden;transition:all .25s var(--ease)}
|
|
187
|
+
.cfg-card:hover{border-color:var(--c-border2);background:rgba(255,183,197,.04)}
|
|
188
|
+
.cfg-card-head{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;background:rgba(255,183,197,.03);cursor:pointer;font-size:14px;font-weight:600;user-select:none;gap:8px;border-bottom:1px solid transparent;transition:.2s}
|
|
189
|
+
.cfg-card-head:hover{background:rgba(255,183,197,.06)}
|
|
190
|
+
.cfg-card.expanded .cfg-card-head{border-bottom-color:var(--c-border)}
|
|
166
191
|
.cfg-card-head .arr{font-size:10px;color:var(--c-faint);transition:transform .25s var(--ease);flex-shrink:0}
|
|
167
192
|
.cfg-card-head .arr.open{transform:rotate(90deg)}
|
|
168
|
-
.cfg-card-body{display:none;padding:
|
|
193
|
+
.cfg-card-body{display:none;padding:4px 18px 16px}
|
|
169
194
|
.cfg-card-body.show{display:block;animation:slideDown .2s var(--ease) both}
|
|
170
|
-
.cfg-field{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;margin-bottom:
|
|
171
|
-
.cfg-field:hover{background:rgba(255,
|
|
172
|
-
.cfg-field .k{font-size:11px;color:var(--c-faint);font-weight:500;letter-spacing:.3px;flex-shrink:0}
|
|
195
|
+
.cfg-field{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;margin-bottom:3px;border-radius:8px;background:rgba(255,255,255,.012);gap:8px;transition:all .15s var(--ease)}
|
|
196
|
+
.cfg-field:hover{background:rgba(255,183,197,.04)}
|
|
197
|
+
.cfg-field .k{font-size:11px;color:var(--c-faint);font-weight:500;letter-spacing:.3px;flex-shrink:0;min-width:60px}
|
|
173
198
|
.cfg-field .v{font-size:12.5px;text-align:right;word-break:break-all;min-width:0;flex:1}
|
|
174
|
-
.cfg-field .v code{color:var(--c-
|
|
199
|
+
.cfg-field .v code{color:var(--c-pink);font-family:'SF Mono',Fira Code,monospace;font-size:11.5px}
|
|
175
200
|
.cfg-field .v .num{color:var(--c-accent)}
|
|
176
|
-
.cfg-field .edit-btn{flex-shrink:0;width:
|
|
201
|
+
.cfg-field .edit-btn{flex-shrink:0;width:26px;height:26px;display:flex;align-items:center;justify-content:center;border:1px solid transparent;border-radius:6px;background:transparent;color:var(--c-faint);cursor:pointer;font-size:12px;transition:all .15s;opacity:0}
|
|
177
202
|
.cfg-field:hover .edit-btn{opacity:1;border-color:var(--c-border)}
|
|
178
203
|
.cfg-field .edit-btn:hover{color:var(--c-accent);border-color:var(--c-accent);background:var(--c-accent2)}
|
|
179
|
-
|
|
180
|
-
.cfg-field.
|
|
181
|
-
.cfg-field.
|
|
204
|
+
|
|
205
|
+
.cfg-field.editing{background:rgba(255,154,162,.06);border:1px solid rgba(255,154,162,.18);border-radius:8px;flex-wrap:wrap;padding:10px 12px}
|
|
206
|
+
.cfg-field.editing .v{display:none}
|
|
207
|
+
.cfg-field.editing .edit-btn{display:none}
|
|
182
208
|
.cfg-field .edit-row{display:none;width:100%;gap:6px}
|
|
183
|
-
.cfg-field.
|
|
209
|
+
.cfg-field.editing .edit-row{display:flex;align-items:center}
|
|
184
210
|
.cfg-field .edit-row input,.cfg-field .edit-row select{flex:1;padding:7px 10px;background:rgba(0,0,0,.25);border:1px solid var(--c-border);border-radius:6px;color:var(--c-text);font-size:12.5px;font-family:inherit;outline:none;transition:border-color .15s}
|
|
185
211
|
.cfg-field .edit-row input:focus,.cfg-field .edit-row select:focus{border-color:var(--c-accent)}
|
|
186
212
|
.cfg-field .edit-row .btn-xs{padding:5px 10px;font-size:11px;border-radius:6px;border:1px solid var(--c-border);background:var(--c-surface2);color:var(--c-text);cursor:pointer;transition:all .15s;white-space:nowrap}
|
|
187
|
-
.cfg-field .edit-row .btn-xs.save{background:
|
|
213
|
+
.cfg-field .edit-row .btn-xs.save{background:linear-gradient(135deg,#ff9aa2,#ff8589);border-color:transparent;color:#fff}
|
|
188
214
|
.cfg-field .edit-row .btn-xs:hover{opacity:.85}
|
|
189
|
-
.cfg-actions{display:flex;gap:
|
|
215
|
+
.cfg-actions{display:flex;gap:10px;margin-top:22px;padding-top:18px;border-top:1px solid var(--c-border)}
|
|
190
216
|
|
|
191
217
|
/* Modal */
|
|
192
|
-
.modal-mask{position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.55);padding:24px;animation:fadeIn .2s both;backdrop-filter:blur(6px)
|
|
193
|
-
.modal{background:var(--c-surface);border:1px solid var(--c-border);border-radius:
|
|
218
|
+
.modal-mask{position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.55);padding:24px;animation:fadeIn .2s both;backdrop-filter:blur(6px)}
|
|
219
|
+
.modal{background:var(--c-surface);border:1px solid var(--c-border);border-radius:18px;padding:32px;width:100%;max-width:560px;max-height:85vh;overflow-y:auto;backdrop-filter:blur(28px);box-shadow:0 24px 80px rgba(0,0,0,.5);animation:scaleIn .3s var(--ease) both}
|
|
194
220
|
.modal h3{font-size:18px;font-weight:600;margin-bottom:24px;letter-spacing:-.3px}
|
|
195
221
|
.f-row{margin-bottom:14px}
|
|
196
222
|
.f-row label{display:block;font-size:11px;color:var(--c-dim);margin-bottom:6px;letter-spacing:.5px;font-weight:500}
|
|
197
|
-
.f-row input,.f-row select,.f-row textarea{width:100%;padding:10px 13px;background:rgba(255,255,255,.03);border:1px solid var(--c-border);border-radius:8px;color:var(--c-text);font-size:13px;font-family:inherit;transition:all .2s
|
|
223
|
+
.f-row input,.f-row select,.f-row textarea{width:100%;padding:10px 13px;background:rgba(255,255,255,.03);border:1px solid var(--c-border);border-radius:8px;color:var(--c-text);font-size:13px;font-family:inherit;transition:all .2s;resize:vertical}
|
|
198
224
|
.f-row input:focus,.f-row select:focus,.f-row textarea:focus{outline:none;border-color:var(--c-accent);box-shadow:0 0 0 3px var(--c-accent-glow)}
|
|
199
225
|
.f-row textarea{min-height:80px}
|
|
200
226
|
.f-row textarea.mono{font-family:'SF Mono',Fira Code,monospace;font-size:12px}
|
|
201
227
|
.modal-btns{display:flex;justify-content:flex-end;gap:8px;margin-top:24px}
|
|
202
228
|
|
|
229
|
+
/* Sub-section style */
|
|
230
|
+
.sub-section{margin-top:16px;padding:16px;background:rgba(255,183,197,.03);border:1px solid var(--c-border);border-radius:12px}
|
|
231
|
+
.sub-section h5{font-size:13px;font-weight:600;margin-bottom:10px;display:flex;align-items:center;gap:6px}
|
|
232
|
+
.sub-section h5.gemini{color:var(--c-pink)}
|
|
233
|
+
.sub-section h5.gemini::before{content:'✦'}
|
|
234
|
+
.sub-section h5.tool{color:var(--c-yellow)}
|
|
235
|
+
.sub-section h5.tool::before{content:'◇'}
|
|
236
|
+
|
|
203
237
|
/* Animations */
|
|
204
238
|
.page-enter{animation:fadeUp .35s var(--ease) both}
|
|
205
|
-
@keyframes fadeUp{from{opacity:0;transform:translateY(
|
|
239
|
+
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
|
206
240
|
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
|
|
207
241
|
@keyframes scaleIn{from{opacity:0;transform:scale(.96) translateY(8px)}to{opacity:1;transform:none}}
|
|
208
|
-
@keyframes slideRight{from{opacity:0;transform:translateX(
|
|
242
|
+
@keyframes slideRight{from{opacity:0;transform:translateX(16px)}to{opacity:1;transform:none}}
|
|
209
243
|
@keyframes slideDown{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
|
|
210
244
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
211
|
-
@keyframes
|
|
212
|
-
@keyframes pulse-dot{0%,100%{opacity:1}50%{opacity:.4}}
|
|
245
|
+
@keyframes pulse-dot{0%,100%{opacity:1}50%{opacity:.35}}
|
|
213
246
|
|
|
214
247
|
.mono{font-family:'SF Mono',Fira Code,monospace}
|
|
215
|
-
code{font-family:'SF Mono',Fira Code,monospace;font-size:12px}
|
|
248
|
+
code{font-family:'SF Mono',Fira Code,monospace;font-size:12px;color:var(--c-pink)}
|
|
216
249
|
|
|
217
250
|
/* Responsive */
|
|
218
251
|
@media(max-width:768px){
|
|
@@ -228,6 +261,7 @@ code{font-family:'SF Mono',Fira Code,monospace;font-size:12px}
|
|
|
228
261
|
.card{padding:18px}
|
|
229
262
|
.cfg-grid{grid-template-columns:1fr}
|
|
230
263
|
.modal{max-width:95%;padding:24px}
|
|
264
|
+
.petal{display:none}
|
|
231
265
|
}
|
|
232
266
|
@media(max-width:480px){
|
|
233
267
|
.stats{grid-template-columns:1fr 1fr}
|
|
@@ -237,6 +271,12 @@ code{font-family:'SF Mono',Fira Code,monospace;font-size:12px}
|
|
|
237
271
|
</style>
|
|
238
272
|
</head>
|
|
239
273
|
<body>
|
|
274
|
+
<!-- Sakura petals -->
|
|
275
|
+
<div class="petal">🌸</div><div class="petal">🌸</div><div class="petal">🌸</div>
|
|
276
|
+
<div class="petal">🌸</div><div class="petal">🌸</div><div class="petal">🌸</div>
|
|
277
|
+
<div class="petal">🌸</div><div class="petal">🌸</div><div class="petal">🌸</div>
|
|
278
|
+
<div class="petal">🌸</div>
|
|
279
|
+
|
|
240
280
|
<div id="app"></div>
|
|
241
281
|
<script>
|
|
242
282
|
const{createApp,ref,reactive,computed,onMounted,onUnmounted}=Vue
|
|
@@ -251,21 +291,18 @@ const API=(path,opt={})=>{
|
|
|
251
291
|
|
|
252
292
|
createApp({
|
|
253
293
|
setup(){
|
|
254
|
-
// Auth
|
|
255
294
|
const jwt=ref(localStorage.getItem('chaite_jwt')||'')
|
|
256
295
|
const loggedIn=ref(!!jwt.value)
|
|
257
296
|
const loginInput=ref('')
|
|
258
297
|
const loginLoading=ref(false)
|
|
259
298
|
const loginError=ref('')
|
|
260
299
|
|
|
261
|
-
// UI state
|
|
262
300
|
const page=ref('dashboard')
|
|
263
301
|
const sidebarOpen=ref(false)
|
|
264
302
|
const loading=ref(true)
|
|
265
303
|
const refreshTime=ref('')
|
|
266
304
|
let timer=null
|
|
267
305
|
|
|
268
|
-
// Data
|
|
269
306
|
const health=ref(null)
|
|
270
307
|
const stats=ref(null)
|
|
271
308
|
const channels=ref([])
|
|
@@ -278,23 +315,19 @@ createApp({
|
|
|
278
315
|
const conversations=ref([])
|
|
279
316
|
const convDetail=ref(null)
|
|
280
317
|
|
|
281
|
-
// Modal
|
|
282
318
|
const modal=ref(null)
|
|
283
319
|
const editing=ref(null)
|
|
284
320
|
const form=ref({})
|
|
285
321
|
|
|
286
|
-
// Config edit
|
|
287
322
|
const editingConfig=ref(false)
|
|
288
323
|
const configForm=ref('{}')
|
|
289
324
|
const editingCfgField=ref(null)
|
|
290
325
|
|
|
291
|
-
// Toast
|
|
292
326
|
const toastMsg=ref('')
|
|
293
327
|
const toastType=ref('ok')
|
|
294
328
|
let toastTimer=null
|
|
295
329
|
function toast(msg,type='ok'){toastMsg.value=msg;toastType.value=type;clearTimeout(toastTimer);toastTimer=setTimeout(()=>toastMsg.value='',3000)}
|
|
296
330
|
|
|
297
|
-
// Login
|
|
298
331
|
async function doLogin(){
|
|
299
332
|
loginError.value='';loginLoading.value=true
|
|
300
333
|
try{
|
|
@@ -313,7 +346,6 @@ createApp({
|
|
|
313
346
|
}
|
|
314
347
|
function logout(){jwt.value='';localStorage.removeItem('chaite_jwt');loggedIn.value=false;clearInterval(timer)}
|
|
315
348
|
|
|
316
|
-
// Data fetch
|
|
317
349
|
async function refreshAll(){
|
|
318
350
|
loading.value=true
|
|
319
351
|
try{
|
|
@@ -329,58 +361,51 @@ createApp({
|
|
|
329
361
|
API('/api/config').catch(()=>({})),
|
|
330
362
|
API('/api/state/conversations/list').catch(()=>({})),
|
|
331
363
|
])
|
|
332
|
-
health.value=h.data||null
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
presets.value=p.data||[]
|
|
337
|
-
processors.value=pr.data||[]
|
|
338
|
-
triggers.value=tr.data||[]
|
|
339
|
-
toolGroups.value=tg.data||[]
|
|
340
|
-
config.value=cfg.data||{}
|
|
341
|
-
conversations.value=cv.data||[]
|
|
342
|
-
refreshTime.value=new Date().toLocaleTimeString()
|
|
364
|
+
health.value=h.data||null;stats.value=s.data||null;channels.value=c.data||[]
|
|
365
|
+
tools.value=t.data||[];presets.value=p.data||[];processors.value=pr.data||[]
|
|
366
|
+
triggers.value=tr.data||[];toolGroups.value=tg.data||[];config.value=cfg.data||{}
|
|
367
|
+
conversations.value=cv.data||[];refreshTime.value=new Date().toLocaleTimeString()
|
|
343
368
|
}catch(e){console.error(e)}
|
|
344
369
|
finally{loading.value=false}
|
|
345
370
|
}
|
|
346
371
|
|
|
347
372
|
function navigate(p){page.value=p;sidebarOpen.value=false}
|
|
348
|
-
|
|
349
|
-
// Helpers
|
|
350
|
-
function uptimeStr(s){
|
|
351
|
-
if(!s)return '-'
|
|
352
|
-
const h=Math.floor(s/3600),m=Math.floor(s%3600/60),sec=s%60
|
|
353
|
-
return h>0?`${h}h ${m}m ${sec}s`:`${m}m ${sec}s`
|
|
354
|
-
}
|
|
373
|
+
function uptimeStr(s){if(!s)return'-';const h=Math.floor(s/3600),m=Math.floor(s%3600/60),sec=s%60;return h>0?`${h}h ${m}m ${sec}s`:`${m}m ${sec}s`}
|
|
355
374
|
function fmtNum(n){if(n>=1e6)return(n/1e6).toFixed(1)+'M';if(n>=1e3)return(n/1e3).toFixed(1)+'K';return String(n||0)}
|
|
356
375
|
function fLen(s,n){return s&&s.length>n?s.slice(0,n)+'...':s||''}
|
|
357
376
|
|
|
358
|
-
// Channel test
|
|
359
377
|
async function testChannel(id){
|
|
360
378
|
const d=await API('/api/system/test-channel',{method:'POST',body:JSON.stringify({channelId:id})})
|
|
361
|
-
if(d.data?.status==='ok')toast(
|
|
362
|
-
else toast(
|
|
379
|
+
if(d.data?.status==='ok')toast(d.data.channelName+' — '+d.data.latency+'ms')
|
|
380
|
+
else toast(d.data?.error||d.message,'err')
|
|
363
381
|
}
|
|
364
382
|
|
|
365
|
-
// CRUD
|
|
366
383
|
function openModal(type,item=null){
|
|
367
384
|
modal.value=type;editing.value=item
|
|
368
385
|
if(item){
|
|
369
386
|
form.value=JSON.parse(JSON.stringify(item))
|
|
370
387
|
if(type==='channel'){
|
|
371
388
|
form.value._models=(form.value.models||[]).join(', ')
|
|
372
|
-
form.value.
|
|
373
|
-
|
|
374
|
-
|
|
389
|
+
if(!form.value.sendMessageOption)form.value.sendMessageOption={}
|
|
390
|
+
const smo=form.value.sendMessageOption
|
|
391
|
+
smo.model=smo.model||''
|
|
392
|
+
smo.temperature=smo.temperature||0.8
|
|
393
|
+
smo.maxToken=smo.maxToken||4096
|
|
394
|
+
smo.systemOverride=smo.systemOverride||''
|
|
395
|
+
if(smo.enableReasoning===undefined)smo.enableReasoning=false
|
|
396
|
+
smo.reasoningEffort=smo.reasoningEffort||'high'
|
|
397
|
+
if(smo.toolCallLimit===undefined)smo.toolCallLimit={maxConsecutiveCalls:8,maxConsecutiveIdenticalCalls:2}
|
|
398
|
+
if(!smo.responseModalities)smo.responseModalities=[]
|
|
399
|
+
if(!smo.safetySettings)smo.safetySettings=[]
|
|
400
|
+
form.value._responseModalities=JSON.stringify(smo.responseModalities)
|
|
401
|
+
form.value._safetySettings=JSON.stringify(smo.safetySettings,null,2)
|
|
375
402
|
}
|
|
376
403
|
}else{
|
|
377
404
|
form.value={
|
|
378
405
|
channel:{name:'',adapterType:'openai',type:'openai',models:[''],options:{baseUrl:'',apiKey:''},status:'enabled',weight:1,priority:0,sendMessageOption:{model:'',temperature:0.8,maxToken:4096,systemOverride:'',enableReasoning:false,reasoningEffort:'high',reasoningBudgetTokens:0,responseModalities:[],safetySettings:[],toolCallLimit:{maxConsecutiveCalls:8,maxConsecutiveIdenticalCalls:2}}},
|
|
379
|
-
preset:{name:'',prefix:'',sendMessageOption:{model:'',temperature:0.8,maxToken:4096,systemOverride:'',enableReasoning:false,reasoningEffort:'high',disableHistoryRead:false,disableHistorySave:false}},
|
|
380
|
-
tool:{name:'',description:'',code:''},
|
|
381
|
-
|
|
382
|
-
trigger:{name:'',description:'',code:''},
|
|
383
|
-
toolGroup:{name:'',description:'',toolIds:[],status:'enabled',isDefault:false},
|
|
406
|
+
preset:{name:'',prefix:'',sendMessageOption:{model:'',temperature:0.8,maxToken:4096,systemOverride:'',enableReasoning:false,reasoningEffort:'high',disableHistoryRead:false,disableHistorySave:false},groupContext:undefined,disableSystemInstructions:false},
|
|
407
|
+
tool:{name:'',description:'',code:''},processor:{name:'',type:'pre',description:'',code:''},
|
|
408
|
+
trigger:{name:'',description:'',code:''},toolGroup:{name:'',description:'',toolIds:[],status:'enabled',isDefault:false},
|
|
384
409
|
}[type]||{}
|
|
385
410
|
}
|
|
386
411
|
}
|
|
@@ -389,18 +414,19 @@ createApp({
|
|
|
389
414
|
async function saveItem(){
|
|
390
415
|
const apiMap={channel:'/api/channels',preset:'/api/preset',tool:'/api/tools',processor:'/api/processors',trigger:'/api/triggers',toolGroup:'/api/toolGroups'}
|
|
391
416
|
try{
|
|
392
|
-
const type=modal.value
|
|
393
|
-
const
|
|
394
|
-
const url=apiMap[type]+(id?'/'+id:'')
|
|
417
|
+
const type=modal.value,id=editing.value?.id,url=apiMap[type]+(id?'/'+id:'')
|
|
418
|
+
const fd=JSON.parse(JSON.stringify(form.value))
|
|
395
419
|
if(type==='channel'){
|
|
396
|
-
|
|
397
|
-
if(
|
|
398
|
-
if(
|
|
399
|
-
if(formData._safetySettings){try{formData.sendMessageOption.safetySettings=JSON.parse(formData._safetySettings)}catch(e){};delete formData._safetySettings}
|
|
400
|
-
form.value=formData
|
|
420
|
+
if(fd._models){fd.models=fd._models.split(',').map(m=>m.trim()).filter(m=>m);delete fd._models}
|
|
421
|
+
if(fd._responseModalities){try{fd.sendMessageOption.responseModalities=JSON.parse(fd._responseModalities)}catch(e){};delete fd._responseModalities}
|
|
422
|
+
if(fd._safetySettings){try{fd.sendMessageOption.safetySettings=JSON.parse(fd._safetySettings)}catch(e){};delete fd._safetySettings}
|
|
401
423
|
}
|
|
402
|
-
|
|
403
|
-
|
|
424
|
+
if(type==='toolGroup'&&fd._toolIds){
|
|
425
|
+
fd.toolIds=fd._toolIds.split(',').map(m=>m.trim()).filter(m=>m)
|
|
426
|
+
delete fd._toolIds
|
|
427
|
+
}
|
|
428
|
+
const d=await API(url,{method:id?'PUT':'POST',body:JSON.stringify(fd)})
|
|
429
|
+
if(d.data!==undefined){toast('已保存 ✨');closeModal();refreshAll()}
|
|
404
430
|
else toast(d.message||'错误','err')
|
|
405
431
|
}catch(e){toast(e.message,'err')}
|
|
406
432
|
}
|
|
@@ -412,67 +438,102 @@ createApp({
|
|
|
412
438
|
catch(e){toast(e.message,'err')}
|
|
413
439
|
}
|
|
414
440
|
|
|
415
|
-
// Config
|
|
441
|
+
// ===== Config System =====
|
|
416
442
|
const expandedCfg=ref({})
|
|
417
|
-
const
|
|
443
|
+
const SKIP_KEYS=new Set(['version','_saveOrigin','authKey','cloudBaseUrl','cloudApiKey'])
|
|
418
444
|
|
|
419
|
-
// 配置键名翻译表
|
|
420
445
|
const CFG_LABELS={
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
446
|
+
// 配置分组标签
|
|
447
|
+
basic:'基础配置',bym:'伪人模式',llm:'模型与对话',management:'管理控制',
|
|
448
|
+
chaite:'Chaite 面板',memory:'记忆系统',update:'更新相关',
|
|
449
|
+
// 基础配置
|
|
450
|
+
toggleMode:'触发方式',togglePrefix:'触发前缀',debug:'调试模式',commandPrefix:'命令前缀',
|
|
451
|
+
// 伪人模式
|
|
452
|
+
enable:'启用',hit:'必触发词',probability:'概率',defaultPreset:'伪人默认预设',presetPrefix:'预设前缀',
|
|
453
|
+
presetMap:'预设映射',maxTokens:'最大Token',temperature:'温度',sendReasoning:'发送思考',
|
|
454
|
+
// 模型与对话
|
|
455
|
+
defaultModel:'默认对话模型',embeddingModel:'嵌入模型',dimensions:'向量维度',
|
|
456
|
+
defaultChatPresetId:'默认对话预设',enableCustomPreset:'允许切换预设',
|
|
457
|
+
customPresetUserWhiteList:'预设切换用户白名单',customPresetUserBlackList:'预设切换用户黑名单',
|
|
458
|
+
promptBlockWords:'输入屏蔽词',responseBlockWords:'回复屏蔽词',
|
|
459
|
+
blockStrategy:'屏蔽策略',blockWordMask:'屏蔽替换字符',
|
|
460
|
+
enableGroupContext:'群组上下文',groupContextLength:'上下文长度',
|
|
461
|
+
groupContextTemplatePrefix:'上下文模板前缀',groupContextTemplateMessage:'上下文模板消息',
|
|
462
|
+
groupContextTemplateSuffix:'上下文模板后缀',
|
|
463
|
+
// 管理控制
|
|
464
|
+
blackGroups:'群黑名单',whiteGroups:'群白名单',blackUsers:'用户黑名单',
|
|
465
|
+
whiteUsers:'用户白名单',defaultRateLimit:'默认速率限制',
|
|
466
|
+
// Chaite 面板
|
|
467
|
+
dataDir:'数据目录',processorsDirPath:'处理器目录',triggersDir:'触发器目录',
|
|
468
|
+
toolsDirPath:'工具目录',host:'监听地址',port:'监听端口',storage:'存储实现',
|
|
469
|
+
accessToken:'访问令牌',tokenExpiresIn:'令牌有效期(秒)',
|
|
470
|
+
// 记忆系统
|
|
471
|
+
database:'数据库路径',vectorDimensions:'向量维度',
|
|
472
|
+
group:'群组记忆',user:'用户记忆',extensions:'扩展模块',
|
|
473
|
+
// 群组记忆
|
|
474
|
+
enabledGroups:'启用群组',extractionModel:'提取模型',extractionPresetId:'提取预设',
|
|
475
|
+
minMessageCount:'最小消息数',maxMessageWindow:'最大消息窗',
|
|
476
|
+
retrievalMode:'检索模式',hybridPrefer:'混合检索偏好',
|
|
477
|
+
historyPollInterval:'历史轮询间隔',historyBatchSize:'历史批次大小',
|
|
478
|
+
promptHeader:'提示词头部',promptItemTemplate:'提示词条目模板',promptFooter:'提示词尾部',
|
|
479
|
+
extractionSystemPrompt:'提取系统提示',extractionUserPrompt:'提取用户提示',
|
|
480
|
+
vectorMaxDistance:'向量最大距离',textMaxBm25Score:'文本BM25最大分数',
|
|
481
|
+
maxFactsPerInjection:'最大注入事实数',minImportanceForInjection:'最小注入重要度',
|
|
482
|
+
// 用户记忆
|
|
483
|
+
whitelist:'白名单',blacklist:'黑名单',
|
|
484
|
+
maxItemsPerInjection:'最大注入条目',maxRelevantItemsPerQuery:'最大检索条目',
|
|
485
|
+
// 扩展
|
|
486
|
+
simple:'简易分词',libraryPath:'库路径',dictPath:'词典路径',useJieba:'使用结巴分词',
|
|
441
487
|
}
|
|
488
|
+
function cfgLabel(k){return CFG_LABELS[k]||k.charAt(0).toUpperCase()+k.slice(1)}
|
|
442
489
|
|
|
443
490
|
const cfgSections=computed(()=>{
|
|
444
491
|
const secs=[]
|
|
445
492
|
const order=['basic','bym','llm','management','chaite','memory','update']
|
|
446
493
|
for(const key of order){
|
|
447
494
|
const v=config.value[key]
|
|
448
|
-
if(v!==undefined) secs.push({key,label:cfgLabel(key),fields:
|
|
495
|
+
if(v!==undefined) secs.push({key,label:cfgLabel(key),fields:v,isObj:typeof v==='object'&&!Array.isArray(v)})
|
|
449
496
|
}
|
|
450
497
|
for(const[k,v]of Object.entries(config.value)){
|
|
451
|
-
if(order.includes(k)||
|
|
452
|
-
secs.push({key:k,label:cfgLabel(k),fields:
|
|
498
|
+
if(order.includes(k)||SKIP_KEYS.has(k))continue
|
|
499
|
+
secs.push({key:k,label:cfgLabel(k),fields:v,isObj:typeof v==='object'&&!Array.isArray(v)})
|
|
453
500
|
}
|
|
454
501
|
return secs
|
|
455
502
|
})
|
|
456
503
|
function toggleCfg(k){expandedCfg.value[k]=!expandedCfg.value[k]}
|
|
457
504
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const raw=typeof currentVal==='string'?currentVal:JSON.stringify(currentVal)
|
|
461
|
-
editingCfgField.value={sec:secKey,key:fieldKey,val:raw,type:typeof currentVal==='boolean'?'bool':typeof currentVal==='number'?'num':'str'}
|
|
505
|
+
function startEditField(secKey,fieldKey,currentVal){
|
|
506
|
+
const isArr=Array.isArray(currentVal)
|
|
507
|
+
const raw=isArr?JSON.stringify(currentVal):(typeof currentVal==='string'?currentVal:JSON.stringify(currentVal))
|
|
508
|
+
editingCfgField.value={sec:secKey,key:fieldKey,val:raw,type:typeof currentVal==='boolean'?'bool':typeof currentVal==='number'?'num':isArr?'arr':'str'}
|
|
462
509
|
}
|
|
463
510
|
function cancelEditField(){editingCfgField.value=null}
|
|
464
|
-
|
|
511
|
+
// 深层设置对象值,支持 'a.b.c' 路径
|
|
512
|
+
function deepSet(obj,path,val){
|
|
513
|
+
const keys=path.split('.')
|
|
514
|
+
let cur=obj
|
|
515
|
+
for(let i=0;i<keys.length-1;i++){
|
|
516
|
+
if(!cur[keys[i]])cur[keys[i]]={}
|
|
517
|
+
cur=cur[keys[i]]
|
|
518
|
+
}
|
|
519
|
+
cur[keys[keys.length-1]]=val
|
|
520
|
+
}
|
|
521
|
+
async function saveEditField(secKey,fieldKey){
|
|
465
522
|
if(!editingCfgField.value)return
|
|
466
523
|
let newVal=editingCfgField.value.val
|
|
467
524
|
const type=editingCfgField.value.type
|
|
468
|
-
if(type==='bool')
|
|
469
|
-
else if(type==='num')
|
|
525
|
+
if(type==='bool')newVal=newVal==='true'||newVal===true
|
|
526
|
+
else if(type==='num')newVal=Number(newVal)
|
|
527
|
+
else if(type==='arr'){try{newVal=JSON.parse(newVal)}catch(e){toast('数组格式错误,请使用 JSON 数组格式','err');return}}
|
|
470
528
|
try{
|
|
471
|
-
// Deep set the nested key
|
|
472
529
|
const cfgCopy=JSON.parse(JSON.stringify(config.value))
|
|
473
|
-
|
|
530
|
+
if(fieldKey.includes('.')){
|
|
531
|
+
deepSet(cfgCopy[secKey],fieldKey,newVal)
|
|
532
|
+
}else{
|
|
533
|
+
cfgCopy[secKey][fieldKey]=newVal
|
|
534
|
+
}
|
|
474
535
|
const d=await API('/api/config',{method:'POST',body:JSON.stringify(cfgCopy)})
|
|
475
|
-
if(d.data!==undefined){toast('配置已保存');cancelEditField();refreshAll()}
|
|
536
|
+
if(d.data!==undefined){toast('配置已保存 ✨');cancelEditField();refreshAll()}
|
|
476
537
|
else toast(d.message||'错误','err')
|
|
477
538
|
}catch(e){toast('保存失败: '+e.message,'err')}
|
|
478
539
|
}
|
|
@@ -482,12 +543,11 @@ createApp({
|
|
|
482
543
|
try{
|
|
483
544
|
const obj=JSON.parse(configForm.value)
|
|
484
545
|
const d=await API('/api/config',{method:'POST',body:JSON.stringify(obj)})
|
|
485
|
-
if(d.data!==undefined){toast('配置已保存');editingConfig.value=false;refreshAll()}
|
|
546
|
+
if(d.data!==undefined){toast('配置已保存 ✨');editingConfig.value=false;refreshAll()}
|
|
486
547
|
else toast(d.message||'错误','err')
|
|
487
548
|
}catch(e){toast('无效 JSON: '+e.message,'err')}
|
|
488
549
|
}
|
|
489
550
|
|
|
490
|
-
// Conversations
|
|
491
551
|
async function viewConv(uid){
|
|
492
552
|
try{const d=await API('/api/state/'+encodeURIComponent(uid));convDetail.value=d.data||null}
|
|
493
553
|
catch(e){toast(e.message,'err')}
|
|
@@ -508,27 +568,38 @@ createApp({
|
|
|
508
568
|
})
|
|
509
569
|
onUnmounted(()=>clearInterval(timer))
|
|
510
570
|
|
|
571
|
+
// Helper: flatten object fields for display, handling nested objects
|
|
572
|
+
function flattenFields(obj,prefix=''){
|
|
573
|
+
if(!obj||typeof obj!=='object')return{[prefix]:obj}
|
|
574
|
+
if(Array.isArray(obj))return{[prefix]:obj}
|
|
575
|
+
const result={}
|
|
576
|
+
for(const[k,v]of Object.entries(obj)){
|
|
577
|
+
if(v&&typeof v==='object'&&!Array.isArray(v)){
|
|
578
|
+
const nested=flattenFields(v,k+'.')
|
|
579
|
+
Object.assign(result,nested)
|
|
580
|
+
}else{result[prefix+k]=v}
|
|
581
|
+
}
|
|
582
|
+
return result
|
|
583
|
+
}
|
|
584
|
+
|
|
511
585
|
return{
|
|
512
586
|
jwt,loggedIn,loginInput,loginLoading,loginError,doLogin,logout,
|
|
513
587
|
page,sidebarOpen,navigate,loading,refreshTime,isHealthy,
|
|
514
588
|
health,stats,channels,tools,presets,processors,triggers,toolGroups,config,conversations,convDetail,
|
|
515
|
-
channelsOk,
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
viewConv,clearConv,
|
|
520
|
-
toastMsg,toastType,uptimeStr,fmtNum,fLen,refreshAll,
|
|
589
|
+
channelsOk,modal,editing,form,openModal,closeModal,saveItem,deleteItem,testChannel,
|
|
590
|
+
expandedCfg,cfgSections,toggleCfg,editConfig,editingConfig,configForm,saveConfig,
|
|
591
|
+
editingCfgField,startEditField,cancelEditField,saveEditField,cfgLabel,flattenFields,
|
|
592
|
+
viewConv,clearConv,toastMsg,toastType,uptimeStr,fmtNum,fLen,refreshAll,
|
|
521
593
|
}
|
|
522
594
|
},
|
|
523
595
|
template:`
|
|
524
|
-
<!-- Toast -->
|
|
525
596
|
<div v-if="toastMsg" :class="['toast',toastType]">{{toastMsg}}</div>
|
|
526
597
|
|
|
527
598
|
<!-- Login -->
|
|
528
599
|
<div v-if="!loggedIn" class="login-wrap">
|
|
529
600
|
<div class="login-card">
|
|
530
601
|
<h1>Chaite</h1>
|
|
531
|
-
<p class="sub"
|
|
602
|
+
<p class="sub">✦ 管理控制台 ✦</p>
|
|
532
603
|
<div v-if="!loginLoading">
|
|
533
604
|
<input v-model="loginInput" type="password" placeholder="输入访问令牌..."
|
|
534
605
|
@keyup.enter="doLogin" autofocus/>
|
|
@@ -540,13 +611,9 @@ createApp({
|
|
|
540
611
|
</div>
|
|
541
612
|
</div>
|
|
542
613
|
|
|
543
|
-
<!-- Main
|
|
614
|
+
<!-- Main -->
|
|
544
615
|
<div v-else class="layout">
|
|
545
|
-
|
|
546
|
-
<!-- Sidebar overlay (mobile) -->
|
|
547
616
|
<div :class="['sidebar-overlay',sidebarOpen&&'show']" @click="sidebarOpen=false"></div>
|
|
548
|
-
|
|
549
|
-
<!-- Sidebar -->
|
|
550
617
|
<aside :class="['sidebar',sidebarOpen&&'open']">
|
|
551
618
|
<div class="sidebar-logo">
|
|
552
619
|
<h2>Chaite</h2>
|
|
@@ -593,9 +660,7 @@ createApp({
|
|
|
593
660
|
</div>
|
|
594
661
|
</aside>
|
|
595
662
|
|
|
596
|
-
<!-- Main -->
|
|
597
663
|
<div class="main">
|
|
598
|
-
<!-- Top bar -->
|
|
599
664
|
<header class="topbar">
|
|
600
665
|
<div style="display:flex;align-items:center;gap:12px">
|
|
601
666
|
<button class="menu-toggle" @click="sidebarOpen=!sidebarOpen">☰</button>
|
|
@@ -606,8 +671,7 @@ createApp({
|
|
|
606
671
|
<div class="meta">
|
|
607
672
|
<span class="refresh-text" v-if="refreshTime">↻ {{refreshTime}}</span>
|
|
608
673
|
<button class="btn ghost" @click="refreshAll" :disabled="loading">
|
|
609
|
-
<span v-if="loading" class="spin"></span>
|
|
610
|
-
<span v-else>↻</span>
|
|
674
|
+
<span v-if="loading" class="spin"></span><span v-else>↻</span>
|
|
611
675
|
</button>
|
|
612
676
|
<button class="btn ghost" @click="logout">⏻</button>
|
|
613
677
|
</div>
|
|
@@ -615,7 +679,7 @@ createApp({
|
|
|
615
679
|
|
|
616
680
|
<div class="content" :key="page">
|
|
617
681
|
|
|
618
|
-
<!--
|
|
682
|
+
<!-- ===== DASHBOARD ===== -->
|
|
619
683
|
<div v-if="page==='dashboard'" class="page-enter">
|
|
620
684
|
<div class="stats" v-if="health">
|
|
621
685
|
<div class="stat"><div class="num">{{uptimeStr(health.uptime)}}</div><div class="lbl">运行时长</div></div>
|
|
@@ -623,7 +687,7 @@ createApp({
|
|
|
623
687
|
<div class="stat"><div class="num">{{health.models?.count||0}}</div><div class="lbl">模型</div></div>
|
|
624
688
|
<div class="stat"><div class="num">{{health.tools?.count||0}}</div><div class="lbl">工具</div></div>
|
|
625
689
|
<div class="stat"><div class="num">{{health.system?.processMemory||0}}M</div><div class="lbl">内存</div></div>
|
|
626
|
-
<div class="stat"><div class="num">{{fmtNum(stats?.summary?.totalCalls)}}</div><div class="lbl"
|
|
690
|
+
<div class="stat"><div class="num">{{fmtNum(stats?.summary?.totalCalls)}}</div><div class="lbl">总调用</div></div>
|
|
627
691
|
</div>
|
|
628
692
|
|
|
629
693
|
<div v-if="!loading&&!health" class="card" style="text-align:center;color:var(--c-dim);padding:48px">无数据 — 服务器是否运行?</div>
|
|
@@ -649,12 +713,7 @@ createApp({
|
|
|
649
713
|
<div class="card-hd"><h3>模型使用统计</h3></div>
|
|
650
714
|
<div class="tbl-wrap"><table class="tbl">
|
|
651
715
|
<thead><tr><th>模型</th><th>调用次数</th><th>Token</th></tr></thead>
|
|
652
|
-
<tbody>
|
|
653
|
-
<tr v-for="(v,k) in stats.perModel" :key="k">
|
|
654
|
-
<td><span class="pill model">{{k}}</span></td>
|
|
655
|
-
<td>{{fmtNum(v.calls)}}</td><td>{{fmtNum(v.tokens)}}</td>
|
|
656
|
-
</tr>
|
|
657
|
-
</tbody>
|
|
716
|
+
<tbody><tr v-for="(v,k) in stats.perModel" :key="k"><td><span class="pill model">{{k}}</span></td><td>{{fmtNum(v.calls)}}</td><td>{{fmtNum(v.tokens)}}</td></tr></tbody>
|
|
658
717
|
</table></div>
|
|
659
718
|
</div>
|
|
660
719
|
|
|
@@ -662,12 +721,7 @@ createApp({
|
|
|
662
721
|
<div class="card-hd"><h3>渠道使用统计</h3></div>
|
|
663
722
|
<div class="tbl-wrap"><table class="tbl">
|
|
664
723
|
<thead><tr><th>渠道</th><th>调用次数</th><th>Token</th><th>模型</th></tr></thead>
|
|
665
|
-
<tbody>
|
|
666
|
-
<tr v-for="(v,k) in stats.perChannel" :key="k">
|
|
667
|
-
<td>{{k}}</td><td>{{fmtNum(v.calls)}}</td><td>{{fmtNum(v.tokens)}}</td>
|
|
668
|
-
<td><span v-for="m in (v.models||[])" class="pill model">{{m}}</span></td>
|
|
669
|
-
</tr>
|
|
670
|
-
</tbody>
|
|
724
|
+
<tbody><tr v-for="(v,k) in stats.perChannel" :key="k"><td>{{k}}</td><td>{{fmtNum(v.calls)}}</td><td>{{fmtNum(v.tokens)}}</td><td><span v-for="m in (v.models||[])" class="pill model">{{m}}</span></td></tr></tbody>
|
|
671
725
|
</table></div>
|
|
672
726
|
</div>
|
|
673
727
|
|
|
@@ -677,7 +731,7 @@ createApp({
|
|
|
677
731
|
</div>
|
|
678
732
|
</div>
|
|
679
733
|
|
|
680
|
-
<!--
|
|
734
|
+
<!-- ===== CHANNELS ===== -->
|
|
681
735
|
<div v-if="page==='channels'" class="page-enter">
|
|
682
736
|
<div class="card">
|
|
683
737
|
<div class="card-hd"><h3>渠道</h3><button class="btn pri sm" @click="openModal('channel')">+ 新建</button></div>
|
|
@@ -689,11 +743,7 @@ createApp({
|
|
|
689
743
|
<td>{{ch.name}}</td><td>{{ch.adapterType}}</td>
|
|
690
744
|
<td><span v-for="m in (ch.models||[])" class="pill model">{{m}}</span></td>
|
|
691
745
|
<td>{{ch.priority}}</td><td>{{fmtNum(ch.statistics?.callTimes)}}</td>
|
|
692
|
-
<td>
|
|
693
|
-
<button class="btn sm" @click="testChannel(ch.id)">测试</button>
|
|
694
|
-
<button class="btn sm" @click="openModal('channel',ch)">编辑</button>
|
|
695
|
-
<button class="btn sm danger" @click="deleteItem('channel',ch.id)">删除</button>
|
|
696
|
-
</td>
|
|
746
|
+
<td><button class="btn sm" @click="testChannel(ch.id)">测试</button><button class="btn sm" @click="openModal('channel',ch)">编辑</button><button class="btn sm danger" @click="deleteItem('channel',ch.id)">删除</button></td>
|
|
697
747
|
</tr>
|
|
698
748
|
<tr v-if="!channels.length"><td colspan="7" style="color:var(--c-dim);text-align:center;padding:32px">暂无渠道</td></tr>
|
|
699
749
|
</tbody>
|
|
@@ -701,120 +751,94 @@ createApp({
|
|
|
701
751
|
</div>
|
|
702
752
|
</div>
|
|
703
753
|
|
|
704
|
-
<!--
|
|
754
|
+
<!-- ===== TOOLS ===== -->
|
|
705
755
|
<div v-if="page==='tools'" class="page-enter">
|
|
706
756
|
<div class="card">
|
|
707
757
|
<div class="card-hd"><h3>工具</h3><button class="btn pri sm" @click="openModal('tool')">+ 新建</button></div>
|
|
708
758
|
<div class="tbl-wrap"><table class="tbl">
|
|
709
759
|
<thead><tr><th>名称</th><th>描述</th><th></th></tr></thead>
|
|
710
|
-
<tbody>
|
|
711
|
-
|
|
712
|
-
<td>{{t.name||t.id}}</td><td style="max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{t.description}}</td>
|
|
713
|
-
<td><button class="btn sm" @click="openModal('tool',t)">编辑</button><button class="btn sm danger" @click="deleteItem('tool',t.id)">删除</button></td>
|
|
714
|
-
</tr>
|
|
715
|
-
<tr v-if="!tools.length"><td colspan="3" style="color:var(--c-dim);text-align:center;padding:32px">暂无工具</td></tr>
|
|
716
|
-
</tbody>
|
|
760
|
+
<tbody><tr v-for="t in tools" :key="t.id"><td>{{t.name||t.id}}</td><td style="max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{t.description}}</td><td><button class="btn sm" @click="openModal('tool',t)">编辑</button><button class="btn sm danger" @click="deleteItem('tool',t.id)">删除</button></td></tr>
|
|
761
|
+
<tr v-if="!tools.length"><td colspan="3" style="color:var(--c-dim);text-align:center;padding:32px">暂无工具</td></tr></tbody>
|
|
717
762
|
</table></div>
|
|
718
763
|
</div>
|
|
719
764
|
</div>
|
|
720
765
|
|
|
721
|
-
<!--
|
|
766
|
+
<!-- ===== PRESETS ===== -->
|
|
722
767
|
<div v-if="page==='presets'" class="page-enter">
|
|
723
768
|
<div class="card">
|
|
724
769
|
<div class="card-hd"><h3>预设</h3><button class="btn pri sm" @click="openModal('preset')">+ 新建</button></div>
|
|
725
770
|
<div class="tbl-wrap"><table class="tbl">
|
|
726
|
-
<thead><tr><th>名称</th><th
|
|
727
|
-
<tbody>
|
|
728
|
-
<
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
</tbody>
|
|
771
|
+
<thead><tr><th>名称</th><th>预设 ID</th><th>模型</th><th>温度</th><th>前缀</th><th>群聊</th><th></th></tr></thead>
|
|
772
|
+
<tbody><tr v-for="p in presets" :key="p.id">
|
|
773
|
+
<td>{{p.name}}</td><td><code>{{p.id}}</code></td>
|
|
774
|
+
<td><span class="pill model">{{p.sendMessageOption?.model||'-'}}</span></td><td>{{p.sendMessageOption?.temperature}}</td>
|
|
775
|
+
<td><code v-if="p.prefix" style="font-size:11px" :title="p.prefix">{{fLen(p.prefix,12)}}</code><span v-else style="color:var(--c-faint)">-</span></td>
|
|
776
|
+
<td><span class="pill" :class="p.groupContext==='disabled'?'off':(p.groupContext?'on':'')">{{p.groupContext==='disabled'?'禁用':p.groupContext==='enabled'?'启用':p.groupContext==='use_system'?'系统':'默认'}}</span></td>
|
|
777
|
+
<td><button class="btn sm" @click="openModal('preset',p)">编辑</button><button class="btn sm danger" @click="deleteItem('preset',p.id)">删除</button></td>
|
|
778
|
+
</tr>
|
|
779
|
+
<tr v-if="!presets.length"><td colspan="5" style="color:var(--c-dim);text-align:center;padding:32px">暂无预设</td></tr></tbody>
|
|
736
780
|
</table></div>
|
|
737
781
|
</div>
|
|
738
782
|
</div>
|
|
739
783
|
|
|
740
|
-
<!--
|
|
784
|
+
<!-- ===== PROCESSORS ===== -->
|
|
741
785
|
<div v-if="page==='processors'" class="page-enter">
|
|
742
786
|
<div class="card">
|
|
743
787
|
<div class="card-hd"><h3>处理器</h3><button class="btn pri sm" @click="openModal('processor')">+ 新建</button></div>
|
|
744
788
|
<div class="tbl-wrap"><table class="tbl">
|
|
745
789
|
<thead><tr><th>名称</th><th>类型</th><th>描述</th><th></th></tr></thead>
|
|
746
|
-
<tbody>
|
|
747
|
-
|
|
748
|
-
<td>{{p.name}}</td>
|
|
749
|
-
<td><span class="pill" :style="{background:p.type==='pre'?'rgba(167,139,250,.12)':'rgba(250,204,21,.12)',color:p.type==='pre'?'var(--c-purple)':'var(--c-yellow)'}">{{p.type==='pre'?'前置':'后置'}}</span></td>
|
|
750
|
-
<td>{{p.description}}</td>
|
|
751
|
-
<td><button class="btn sm" @click="openModal('processor',p)">编辑</button><button class="btn sm danger" @click="deleteItem('processor',p.id)">删除</button></td>
|
|
752
|
-
</tr>
|
|
753
|
-
<tr v-if="!processors.length"><td colspan="4" style="color:var(--c-dim);text-align:center;padding:32px">暂无处理器</td></tr>
|
|
754
|
-
</tbody>
|
|
790
|
+
<tbody><tr v-for="p in processors" :key="p.id"><td>{{p.name}}</td><td><span class="pill" :style="{background:p.type==='pre'?'rgba(201,177,255,.12)':'rgba(255,224,160,.12)',color:p.type==='pre'?'var(--c-purple)':'var(--c-yellow)'}">{{p.type==='pre'?'前置':'后置'}}</span></td><td>{{p.description}}</td><td><button class="btn sm" @click="openModal('processor',p)">编辑</button><button class="btn sm danger" @click="deleteItem('processor',p.id)">删除</button></td></tr>
|
|
791
|
+
<tr v-if="!processors.length"><td colspan="4" style="color:var(--c-dim);text-align:center;padding:32px">暂无处理器</td></tr></tbody>
|
|
755
792
|
</table></div>
|
|
756
793
|
</div>
|
|
757
794
|
</div>
|
|
758
795
|
|
|
759
|
-
<!--
|
|
796
|
+
<!-- ===== TRIGGERS ===== -->
|
|
760
797
|
<div v-if="page==='triggers'" class="page-enter">
|
|
761
798
|
<div class="card">
|
|
762
799
|
<div class="card-hd"><h3>触发器</h3><button class="btn pri sm" @click="openModal('trigger')">+ 新建</button></div>
|
|
763
800
|
<div class="tbl-wrap"><table class="tbl">
|
|
764
801
|
<thead><tr><th>名称</th><th>描述</th><th></th></tr></thead>
|
|
765
|
-
<tbody>
|
|
766
|
-
|
|
767
|
-
<td>{{t.name||t.id}}</td><td>{{t.description}}</td>
|
|
768
|
-
<td><button class="btn sm" @click="openModal('trigger',t)">编辑</button><button class="btn sm danger" @click="deleteItem('trigger',t.id)">删除</button></td>
|
|
769
|
-
</tr>
|
|
770
|
-
<tr v-if="!triggers.length"><td colspan="3" style="color:var(--c-dim);text-align:center;padding:32px">暂无触发器</td></tr>
|
|
771
|
-
</tbody>
|
|
802
|
+
<tbody><tr v-for="t in triggers" :key="t.id"><td>{{t.name||t.id}}</td><td>{{t.description}}</td><td><button class="btn sm" @click="openModal('trigger',t)">编辑</button><button class="btn sm danger" @click="deleteItem('trigger',t.id)">删除</button></td></tr>
|
|
803
|
+
<tr v-if="!triggers.length"><td colspan="3" style="color:var(--c-dim);text-align:center;padding:32px">暂无触发器</td></tr></tbody>
|
|
772
804
|
</table></div>
|
|
773
805
|
</div>
|
|
774
806
|
</div>
|
|
775
807
|
|
|
776
|
-
<!--
|
|
808
|
+
<!-- ===== GROUPS ===== -->
|
|
777
809
|
<div v-if="page==='groups'" class="page-enter">
|
|
778
810
|
<div class="card">
|
|
779
811
|
<div class="card-hd"><h3>工具组</h3><button class="btn pri sm" @click="openModal('toolGroup')">+ 新建</button></div>
|
|
780
812
|
<div class="tbl-wrap"><table class="tbl">
|
|
781
813
|
<thead><tr><th>名称</th><th>描述</th><th>默认</th><th></th></tr></thead>
|
|
782
|
-
<tbody>
|
|
783
|
-
|
|
784
|
-
<td>{{g.name}}</td><td>{{g.description}}</td>
|
|
785
|
-
<td><span v-if="g.isDefault" class="pill on">默认</span></td>
|
|
786
|
-
<td><button class="btn sm" @click="openModal('toolGroup',g)">编辑</button><button class="btn sm danger" @click="deleteItem('toolGroup',g.id)">删除</button></td>
|
|
787
|
-
</tr>
|
|
788
|
-
<tr v-if="!toolGroups.length"><td colspan="4" style="color:var(--c-dim);text-align:center;padding:32px">暂无工具组</td></tr>
|
|
789
|
-
</tbody>
|
|
814
|
+
<tbody><tr v-for="g in toolGroups" :key="g.id"><td>{{g.name}}</td><td>{{g.description}}</td><td><span v-if="g.isDefault" class="pill on">默认</span></td><td><button class="btn sm" @click="openModal('toolGroup',g)">编辑</button><button class="btn sm danger" @click="deleteItem('toolGroup',g.id)">删除</button></td></tr>
|
|
815
|
+
<tr v-if="!toolGroups.length"><td colspan="4" style="color:var(--c-dim);text-align:center;padding:32px">暂无工具组</td></tr></tbody>
|
|
790
816
|
</table></div>
|
|
791
817
|
</div>
|
|
792
818
|
</div>
|
|
793
819
|
|
|
794
|
-
<!--
|
|
820
|
+
<!-- ===== SESSIONS ===== -->
|
|
795
821
|
<div v-if="page==='conversations'" class="page-enter">
|
|
796
822
|
<div class="card">
|
|
797
823
|
<div class="card-hd"><h3>活跃会话</h3></div>
|
|
798
824
|
<div class="tbl-wrap"><table class="tbl">
|
|
799
825
|
<thead><tr><th>用户 ID</th><th>对话 ID</th><th>模型</th><th></th></tr></thead>
|
|
800
|
-
<tbody>
|
|
801
|
-
|
|
802
|
-
<td>{{c.userId}}</td><td><code style="font-size:11px">{{c.conversationId||'-'}}</code></td>
|
|
803
|
-
<td><span v-if="c.currentModel" class="pill model">{{c.currentModel}}</span><span v-else style="color:var(--c-dim)">-</span></td>
|
|
804
|
-
<td><button class="btn sm" @click="viewConv(c.userId)">查看</button><button class="btn sm danger" @click="clearConv(c.userId)">清除</button></td>
|
|
805
|
-
</tr>
|
|
806
|
-
<tr v-if="!conversations.length"><td colspan="4" style="color:var(--c-dim);text-align:center;padding:32px">暂无活跃会话</td></tr>
|
|
807
|
-
</tbody>
|
|
826
|
+
<tbody><tr v-for="c in conversations" :key="c.userId"><td>{{c.userId}}</td><td><code style="font-size:11px">{{c.conversationId||'-'}}</code></td><td><span v-if="c.currentModel" class="pill model">{{c.currentModel}}</span><span v-else style="color:var(--c-dim)">-</span></td><td><button class="btn sm" @click="viewConv(c.userId)">查看</button><button class="btn sm danger" @click="clearConv(c.userId)">清除</button></td></tr>
|
|
827
|
+
<tr v-if="!conversations.length"><td colspan="4" style="color:var(--c-dim);text-align:center;padding:32px">暂无活跃会话</td></tr></tbody>
|
|
808
828
|
</table></div>
|
|
809
829
|
</div>
|
|
810
830
|
</div>
|
|
811
831
|
|
|
812
|
-
<!--
|
|
832
|
+
<!-- ===== CONFIG (redesigned) ===== -->
|
|
813
833
|
<div v-if="page==='config'" class="page-enter">
|
|
814
834
|
<div class="card">
|
|
815
|
-
<div class="card-hd"
|
|
835
|
+
<div class="card-hd">
|
|
836
|
+
<h3>插件配置</h3>
|
|
837
|
+
<button class="btn sm" @click="editConfig">编辑完整 JSON</button>
|
|
838
|
+
</div>
|
|
816
839
|
<div v-if="cfgSections.length" class="cfg-grid">
|
|
817
|
-
<div v-for="sec in cfgSections" :key="sec.key"
|
|
840
|
+
<div v-for="sec in cfgSections" :key="sec.key"
|
|
841
|
+
:class="['cfg-card',expandedCfg[sec.key]&&'expanded']">
|
|
818
842
|
<div class="cfg-card-head" @click="toggleCfg(sec.key)">
|
|
819
843
|
<span>{{sec.label}}</span>
|
|
820
844
|
<span style="display:flex;align-items:center;gap:8px">
|
|
@@ -823,46 +847,47 @@ createApp({
|
|
|
823
847
|
</span>
|
|
824
848
|
</div>
|
|
825
849
|
<div :class="['cfg-card-body',expandedCfg[sec.key]&&'show']">
|
|
826
|
-
<
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
<
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
<
|
|
839
|
-
<
|
|
840
|
-
<
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
850
|
+
<template v-if="sec.isObj">
|
|
851
|
+
<div v-for="(fv,fk) in flattenFields(sec.fields)" :key="fk"
|
|
852
|
+
:class="['cfg-field',editingCfgField&&editingCfgField.sec===sec.key&&editingCfgField.key===fk?'editing':'']">
|
|
853
|
+
<span class="k">{{cfgLabel(fk)}}</span>
|
|
854
|
+
<span class="v">
|
|
855
|
+
<template v-if="typeof fv==='boolean'"><span :class="['pill',fv?'on':'off']">{{fv?'是':'否'}}</span></template>
|
|
856
|
+
<template v-else-if="typeof fv==='number'"><span class="num">{{fv}}</span></template>
|
|
857
|
+
<template v-else-if="Array.isArray(fv)"><code style="font-size:10px">[{{fv.length}}项]</code></template>
|
|
858
|
+
<template v-else-if="typeof fv==='string'&&fv.length<=50"><code>{{fv||'""'}}</code></template>
|
|
859
|
+
<template v-else-if="typeof fv==='string'"><code :title="fv">{{fLen(fv,30)}}</code></template>
|
|
860
|
+
<template v-else><span style="color:var(--c-faint);font-size:11px">对象</span></template>
|
|
861
|
+
</span>
|
|
862
|
+
<button v-if="typeof fv!=='object'||Array.isArray(fv)" class="edit-btn" @click="startEditField(sec.key,fk,fv)" title="编辑">✎</button>
|
|
863
|
+
<div v-if="editingCfgField" class="edit-row">
|
|
864
|
+
<input v-if="Array.isArray(fv)||(typeof fv==='string'&&fv.length>50)" v-model="editingCfgField.val" @keyup.enter="saveEditField(sec.key,fk)"/>
|
|
865
|
+
<input v-else-if="typeof fv==='string'" v-model="editingCfgField.val" @keyup.enter="saveEditField(sec.key,fk)"/>
|
|
866
|
+
<select v-else-if="typeof fv==='boolean'" v-model="editingCfgField.val"><option value="true">是</option><option value="false">否</option></select>
|
|
867
|
+
<input v-else v-model="editingCfgField.val" type="number" @keyup.enter="saveEditField(sec.key,fk)"/>
|
|
868
|
+
<button class="btn-xs save" @click="saveEditField(sec.key,fk)">保存</button>
|
|
869
|
+
<button class="btn-xs" @click="cancelEditField">取消</button>
|
|
870
|
+
</div>
|
|
845
871
|
</div>
|
|
846
|
-
</
|
|
872
|
+
</template>
|
|
873
|
+
<div v-else style="color:var(--c-dim);font-size:13px;padding:8px 0">{{JSON.stringify(sec.fields)}}</div>
|
|
847
874
|
</div>
|
|
848
875
|
</div>
|
|
849
876
|
</div>
|
|
850
877
|
<div v-else style="color:var(--c-dim);font-size:13px;padding:20px 0">暂无配置数据</div>
|
|
851
|
-
<div class="cfg-actions"><button class="btn sm" @click="editConfig">编辑完整 JSON</button></div>
|
|
852
878
|
</div>
|
|
853
879
|
</div>
|
|
854
880
|
|
|
855
|
-
</div
|
|
856
|
-
</div
|
|
857
|
-
</div
|
|
881
|
+
</div>
|
|
882
|
+
</div>
|
|
883
|
+
</div>
|
|
858
884
|
|
|
859
885
|
<!-- ===== MODALS ===== -->
|
|
860
|
-
|
|
861
|
-
<!-- CRUD Modal -->
|
|
862
886
|
<div v-if="modal" class="modal-mask" @click.self="closeModal">
|
|
863
887
|
<div class="modal">
|
|
864
888
|
<h3>{{editing?'编辑':'新建'}} {{modal}}</h3>
|
|
865
889
|
|
|
890
|
+
<!-- Channel -->
|
|
866
891
|
<template v-if="modal==='channel'">
|
|
867
892
|
<div class="f-row"><label>名称</label><input v-model="form.name"/></div>
|
|
868
893
|
<div class="f-row"><label>适配器</label><select v-model="form.adapterType"><option>openai</option><option>gemini</option><option>claude</option></select></div>
|
|
@@ -872,65 +897,51 @@ createApp({
|
|
|
872
897
|
<div class="f-row"><label>优先级</label><input v-model.number="form.priority" type="number"/></div>
|
|
873
898
|
<div class="f-row"><label>权重</label><input v-model.number="form.weight" type="number"/></div>
|
|
874
899
|
<div class="f-row"><label>状态</label><select v-model="form.status"><option>enabled</option><option>disabled</option></select></div>
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
<h4 style="font-size:14px;font-weight:600;margin-bottom:12px;color:var(--c-accent)">模型配置</h4>
|
|
900
|
+
|
|
901
|
+
<div class="sub-section">
|
|
902
|
+
<h5 style="font-size:13px;font-weight:600;margin-bottom:10px;color:var(--c-accent)">✦ 模型配置</h5>
|
|
879
903
|
<div class="f-row"><label>模型名称</label><input v-model="form.sendMessageOption.model"/></div>
|
|
880
|
-
<div class="f-row"><label
|
|
904
|
+
<div class="f-row"><label>温度</label><input v-model.number="form.sendMessageOption.temperature" type="number" step="0.1" min="0" max="2"/></div>
|
|
881
905
|
<div class="f-row"><label>最大 Token</label><input v-model.number="form.sendMessageOption.maxToken" type="number" min="1"/></div>
|
|
882
906
|
<div class="f-row"><label>系统提示词</label><textarea v-model="form.sendMessageOption.systemOverride" placeholder="覆盖系统提示词..."></textarea></div>
|
|
883
|
-
|
|
884
|
-
<div v-if="form.adapterType==='gemini'"
|
|
885
|
-
<h5
|
|
886
|
-
<div class="f-row">
|
|
887
|
-
|
|
888
|
-
<select v-model="form.sendMessageOption.enableReasoning"><option :value="false">关闭</option><option :value="true">开启</option></select>
|
|
889
|
-
</div>
|
|
890
|
-
<div class="f-row" v-if="form.sendMessageOption.enableReasoning">
|
|
891
|
-
<label>思考等级</label>
|
|
892
|
-
<select v-model="form.sendMessageOption.reasoningEffort">
|
|
893
|
-
<option value="minimal">极低(低延迟)</option><option value="low">低</option><option value="medium">中等(平衡)</option><option value="high">高(深度推理)</option>
|
|
894
|
-
</select>
|
|
895
|
-
</div>
|
|
896
|
-
</div>
|
|
897
|
-
|
|
898
|
-
<div style="margin-top:12px;padding:12px;background:var(--c-surface2);border-radius:8px">
|
|
899
|
-
<h5 style="font-size:13px;font-weight:600;margin-bottom:8px;color:var(--c-yellow)">工具调用限制</h5>
|
|
900
|
-
<div class="f-row"><label>最大连续调用次数</label><input v-model.number="form.sendMessageOption.toolCallLimit.maxConsecutiveCalls" type="number" min="1"/></div>
|
|
901
|
-
<div class="f-row"><label>最大连续相同调用次数</label><input v-model.number="form.sendMessageOption.toolCallLimit.maxConsecutiveIdenticalCalls" type="number" min="1"/></div>
|
|
907
|
+
|
|
908
|
+
<div v-if="form.adapterType==='gemini'" class="sub-section">
|
|
909
|
+
<h5 class="gemini">Gemini 思考配置</h5>
|
|
910
|
+
<div class="f-row"><label>启用推理</label><select v-model="form.sendMessageOption.enableReasoning"><option :value="false">关闭</option><option :value="true">开启</option></select></div>
|
|
911
|
+
<div class="f-row" v-if="form.sendMessageOption.enableReasoning"><label>思考等级</label><select v-model="form.sendMessageOption.reasoningEffort"><option value="minimal">极低</option><option value="low">低</option><option value="medium">中</option><option value="high">高</option></select></div>
|
|
902
912
|
</div>
|
|
903
|
-
|
|
904
|
-
<div
|
|
905
|
-
<
|
|
906
|
-
<div class="f-row"><label
|
|
913
|
+
|
|
914
|
+
<div class="sub-section">
|
|
915
|
+
<h5 class="tool">工具调用限制</h5>
|
|
916
|
+
<div class="f-row"><label>最大连续调用</label><input v-model.number="form.sendMessageOption.toolCallLimit.maxConsecutiveCalls" type="number" min="1"/></div>
|
|
917
|
+
<div class="f-row"><label>最大连续相同调用</label><input v-model.number="form.sendMessageOption.toolCallLimit.maxConsecutiveIdenticalCalls" type="number" min="1"/></div>
|
|
907
918
|
</div>
|
|
919
|
+
|
|
920
|
+
<div class="f-row"><label>响应模态 (JSON)</label><input v-model="form._responseModalities" :placeholder="JSON.stringify(form.sendMessageOption.responseModalities||[])"/></div>
|
|
921
|
+
<div class="f-row"><label>安全设置 (JSON)</label><textarea v-model="form._safetySettings" class="mono" style="min-height:50px" :placeholder="JSON.stringify(form.sendMessageOption.safetySettings||[],null,2)"></textarea></div>
|
|
908
922
|
</div>
|
|
909
923
|
</template>
|
|
910
924
|
|
|
925
|
+
<!-- Preset -->
|
|
911
926
|
<template v-if="modal==='preset'">
|
|
912
927
|
<div class="f-row"><label>名称</label><input v-model="form.name"/></div>
|
|
913
928
|
<div class="f-row"><label>前缀</label><input v-model="form.prefix"/></div>
|
|
914
929
|
<div class="f-row"><label>模型</label><input v-model="form.sendMessageOption.model"/></div>
|
|
915
|
-
<div class="f-row"><label
|
|
930
|
+
<div class="f-row"><label>温度</label><input v-model.number="form.sendMessageOption.temperature" type="number" step="0.1" min="0" max="2"/></div>
|
|
916
931
|
<div class="f-row"><label>最大 Token</label><input v-model.number="form.sendMessageOption.maxToken" type="number" min="1"/></div>
|
|
917
932
|
<div class="f-row"><label>系统提示词</label><textarea v-model="form.sendMessageOption.systemOverride" placeholder="覆盖系统提示词..."></textarea></div>
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
<
|
|
921
|
-
<div class="f-row">
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
<div class="f-row" v-
|
|
926
|
-
<label>推理强度</label>
|
|
927
|
-
<select v-model="form.sendMessageOption.reasoningEffort"><option value="minimal">极低</option><option value="low">低</option><option value="medium">中</option><option value="high">高</option></select>
|
|
928
|
-
</div>
|
|
929
|
-
<div class="f-row"><label>禁用读取历史</label><select v-model="form.sendMessageOption.disableHistoryRead"><option :value="false">否</option><option :value="true">是</option></select></div>
|
|
930
|
-
<div class="f-row"><label>禁用保存历史</label><select v-model="form.sendMessageOption.disableHistorySave"><option :value="false">否</option><option :value="true">是</option></select></div>
|
|
933
|
+
<div class="sub-section">
|
|
934
|
+
<h5 style="font-size:13px;font-weight:600;margin-bottom:10px;color:var(--c-accent)">✦ 高级配置</h5>
|
|
935
|
+
<div class="f-row"><label>启用推理</label><select v-model="form.sendMessageOption.enableReasoning"><option :value="false">关闭</option><option :value="true">开启</option></select></div>
|
|
936
|
+
<div class="f-row" v-if="form.sendMessageOption.enableReasoning"><label>推理强度</label><select v-model="form.sendMessageOption.reasoningEffort"><option value="minimal">极低</option><option value="low">低</option><option value="medium">中</option><option value="high">高</option></select></div>
|
|
937
|
+
<div class="f-row"><label>禁用读历史</label><select v-model="form.sendMessageOption.disableHistoryRead"><option :value="false">否</option><option :value="true">是</option></select></div>
|
|
938
|
+
<div class="f-row"><label>禁用写历史</label><select v-model="form.sendMessageOption.disableHistorySave"><option :value="false">否</option><option :value="true">是</option></select></div>
|
|
939
|
+
<div class="f-row"><label>群聊上下文</label><select v-model="form.groupContext"><option :value="undefined">默认</option><option value="enabled">启用</option><option value="disabled">禁用</option><option value="use_system">使用系统预设</option></select></div>
|
|
940
|
+
<div class="f-row"><label>禁用系统提示词</label><select v-model="form.disableSystemInstructions"><option :value="false">否</option><option :value="true">是</option></select></div>
|
|
931
941
|
</div>
|
|
932
942
|
</template>
|
|
933
943
|
|
|
944
|
+
<!-- Tool / Processor / Trigger -->
|
|
934
945
|
<template v-if="modal==='tool'||modal==='processor'||modal==='trigger'">
|
|
935
946
|
<div class="f-row"><label>名称</label><input v-model="form.name"/></div>
|
|
936
947
|
<div class="f-row" v-if="modal==='processor'"><label>类型</label><select v-model="form.type"><option>pre</option><option>post</option></select></div>
|
|
@@ -938,6 +949,7 @@ createApp({
|
|
|
938
949
|
<div class="f-row"><label>代码</label><textarea v-model="form.code" class="mono" style="min-height:200px"></textarea></div>
|
|
939
950
|
</template>
|
|
940
951
|
|
|
952
|
+
<!-- ToolGroup -->
|
|
941
953
|
<template v-if="modal==='toolGroup'">
|
|
942
954
|
<div class="f-row"><label>名称</label><input v-model="form.name"/></div>
|
|
943
955
|
<div class="f-row"><label>描述</label><input v-model="form.description"/></div>
|
|
@@ -955,19 +967,16 @@ createApp({
|
|
|
955
967
|
<!-- Config JSON Edit Modal -->
|
|
956
968
|
<div v-if="editingConfig" class="modal-mask" @click.self="editingConfig=false">
|
|
957
969
|
<div class="modal">
|
|
958
|
-
<h3
|
|
959
|
-
<div class="f-row"><
|
|
960
|
-
<div class="modal-btns">
|
|
961
|
-
<button class="btn" @click="editingConfig=false">取消</button>
|
|
962
|
-
<button class="btn pri" @click="saveConfig">保存</button>
|
|
963
|
-
</div>
|
|
970
|
+
<h3>编辑配置 JSON</h3>
|
|
971
|
+
<div class="f-row"><textarea v-model="configForm" class="mono" style="min-height:400px"></textarea></div>
|
|
972
|
+
<div class="modal-btns"><button class="btn" @click="editingConfig=false">取消</button><button class="btn pri" @click="saveConfig">保存</button></div>
|
|
964
973
|
</div>
|
|
965
974
|
</div>
|
|
966
975
|
|
|
967
976
|
<!-- Session Detail Modal -->
|
|
968
977
|
<div v-if="convDetail" class="modal-mask" @click.self="convDetail=null">
|
|
969
978
|
<div class="modal">
|
|
970
|
-
<h3
|
|
979
|
+
<h3>会话详情 · {{convDetail.userId}}</h3>
|
|
971
980
|
<div class="f-row"><label>对话 ID</label><input :value="convDetail.current?.conversationId||'-'" readonly/></div>
|
|
972
981
|
<div class="f-row"><label>模型</label><input :value="convDetail.current?.model||'-'" readonly/></div>
|
|
973
982
|
<div class="f-row"><label>预设</label><input :value="convDetail.current?.presetId||'-'" readonly/></div>
|