protocol-proxy 2.3.4 → 2.5.0

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/public/index.html CHANGED
@@ -1,277 +1,321 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Protocol Proxy - 协议转换代理管理</title>
7
- <link rel="stylesheet" href="style.css">
8
- </head>
9
- <body>
10
- <div class="container">
11
- <header>
12
- <h1>Protocol Proxy</h1>
13
- <p>OpenAI / Anthropic 协议转换透明代理</p>
14
- </header>
15
-
16
- <div class="stats" id="stats">
17
- <div class="stat-item">
18
- <span class="stat-value" id="stat-total">0</span>
19
- <span class="stat-label">代理配置</span>
20
- </div>
21
- <div class="stat-item">
22
- <span class="stat-value" id="stat-running">0</span>
23
- <span class="stat-label">运行中</span>
24
- </div>
25
- </div>
26
-
27
- <!-- Token 用量统计 -->
28
- <section class="card stats-panel">
29
- <div class="card-header">
30
- <h2>Token 用量统计 <span class="stats-estimated-badge" id="stats-estimated-badge" style="display:none">含估算</span></h2>
31
- <div class="stats-controls">
32
- <div class="model-dropdown" id="stats-proxy-dropdown">
33
- <div class="model-dropdown-trigger stats-filter-trigger" id="stats-proxy-dropdown-trigger">
34
- <span id="stats-proxy-dropdown-value">全部代理</span>
35
- <span class="model-dropdown-arrow">&#9662;</span>
36
- </div>
37
- <div class="model-dropdown-menu" id="stats-proxy-dropdown-menu">
38
- <div class="model-dropdown-options" id="stats-proxy-dropdown-options"></div>
39
- </div>
40
- </div>
41
- <div class="stats-range-btns">
42
- <button class="btn btn-sm stats-range-btn active" data-range="daily">每日</button>
43
- <button class="btn btn-sm stats-range-btn" data-range="monthly">每月</button>
44
- <button class="btn btn-sm stats-range-btn" data-range="yearly">每年</button>
45
- <span class="stats-date-sep">|</span>
46
- <input type="date" id="stats-start-date" class="stats-date-input" placeholder="开始日期">
47
- <span class="stats-date-sep">~</span>
48
- <input type="date" id="stats-end-date" class="stats-date-input" placeholder="结束日期">
49
- </div>
50
- </div>
51
- </div>
52
- <div class="stats-summary" id="stats-summary">
53
- <div class="stats-summary-item">
54
- <span class="stats-summary-value" id="stats-total-tokens">-</span>
55
- <span class="stats-summary-label">总 Token</span>
56
- </div>
57
- <div class="stats-summary-item">
58
- <span class="stats-summary-value" id="stats-prompt-tokens">-</span>
59
- <span class="stats-summary-label">输入 Token</span>
60
- </div>
61
- <div class="stats-summary-item">
62
- <span class="stats-summary-value" id="stats-completion-tokens">-</span>
63
- <span class="stats-summary-label">输出 Token</span>
64
- </div>
65
- <div class="stats-summary-item">
66
- <span class="stats-summary-value" id="stats-total-requests">-</span>
67
- <span class="stats-summary-label">请求数</span>
68
- </div>
69
- </div>
70
- <div id="stats-breakdown" class="stats-breakdown">
71
- <div class="empty">暂无数据</div>
72
- </div>
73
- </section>
74
-
75
- <section class="card">
76
- <div class="card-header">
77
- <h2>代理列表</h2>
78
- <div class="card-header-actions">
79
- <button class="btn" onclick="exportConfig()">导出配置</button>
80
- <button class="btn" onclick="document.getElementById('import-file').click()">导入配置</button>
81
- <input type="file" id="import-file" accept=".json" style="display:none" onchange="handleImportFile(event)">
82
- <button class="btn btn-primary" onclick="openModal()">+ 新建代理</button>
83
- </div>
84
- </div>
85
- <div id="proxy-list" class="proxy-list">
86
- <div class="empty">加载中...</div>
87
- </div>
88
- </section>
89
-
90
- <!-- 编辑/创建弹窗 -->
91
- <div class="modal" id="modal">
92
- <div class="modal-content">
93
- <div class="modal-header">
94
- <h3 id="modal-title">新建代理</h3>
95
- <button class="btn-close" onclick="closeModal()">&times;</button>
96
- </div>
97
- <form id="proxy-form" onsubmit="handleSubmit(event)">
98
- <input type="hidden" id="proxy-id">
99
-
100
- <div class="form-group">
101
- <label>代理名称</label>
102
- <input type="text" id="proxy-name" required placeholder="例如:OpenAI 代理">
103
- </div>
104
-
105
- <div class="form-row">
106
- <div class="form-group">
107
- <label>监听端口</label>
108
- <input type="number" id="proxy-port" required placeholder="8080" min="1000" max="65535">
109
- </div>
110
- <div class="form-group">
111
- <label>Agent 认证</label>
112
- <input type="hidden" id="proxy-auth" value="false">
113
- <div class="model-dropdown" id="auth-dropdown">
114
- <div class="model-dropdown-trigger" id="auth-dropdown-trigger">
115
- <span id="auth-dropdown-value">不启用</span>
116
- <span class="model-dropdown-arrow">&#9662;</span>
117
- </div>
118
- <div class="model-dropdown-menu" id="auth-dropdown-menu">
119
- <div class="model-dropdown-options" id="auth-dropdown-options">
120
- <div class="model-option selected" data-value="false"><span class="model-option-name">不启用</span></div>
121
- <div class="model-option" data-value="true"><span class="model-option-name">启用</span></div>
122
- </div>
123
- </div>
124
- </div>
125
- </div>
126
- </div>
127
-
128
- <div class="form-group" id="auth-token-group" style="display:none">
129
- <label>认证 Token</label>
130
- <input type="text" id="proxy-auth-token" placeholder="Bearer Token">
131
- </div>
132
-
133
- <div class="target-section">
134
- <h4>目标供应商配置</h4>
135
- <div class="target-item">
136
- <input type="hidden" id="provider-id">
137
- <div class="form-row">
138
- <div class="form-group">
139
- <label>供应商</label>
140
- <div class="model-dropdown" id="provider-dropdown">
141
- <div class="model-dropdown-trigger" id="provider-dropdown-trigger">
142
- <span id="provider-dropdown-value">选择供应商...</span>
143
- <span class="model-dropdown-arrow">&#9662;</span>
144
- </div>
145
- <div class="model-dropdown-menu" id="provider-dropdown-menu">
146
- <div class="model-dropdown-options" id="provider-dropdown-options"></div>
147
- <div class="model-add-section">
148
- <input type="text" class="model-add-input" id="provider-add-name" placeholder="供应商名称">
149
- <input type="text" class="model-add-input" id="provider-add-url" placeholder="https://api.example.com">
150
- <button type="button" class="btn btn-primary btn-sm" id="provider-add-btn">添加</button>
151
- </div>
152
- </div>
153
- </div>
154
- </div>
155
- <div class="form-group">
156
- <label>协议</label>
157
- <input type="hidden" id="target-protocol" value="openai">
158
- <div class="model-dropdown" id="protocol-dropdown">
159
- <div class="model-dropdown-trigger" id="protocol-dropdown-trigger">
160
- <span id="protocol-dropdown-value">OpenAI</span>
161
- <span class="model-dropdown-arrow">&#9662;</span>
162
- </div>
163
- <div class="model-dropdown-menu" id="protocol-dropdown-menu">
164
- <div class="model-dropdown-options" id="protocol-dropdown-options">
165
- <div class="model-option selected" data-value="openai"><span class="model-option-name">OpenAI</span></div>
166
- <div class="model-option" data-value="anthropic"><span class="model-option-name">Anthropic</span></div>
167
- <div class="model-option" data-value="gemini"><span class="model-option-name">Gemini</span></div>
168
- </div>
169
- </div>
170
- </div>
171
- </div>
172
- </div>
173
- <div class="form-row">
174
- <div class="form-group">
175
- <label>默认 Model(可选)</label>
176
- <input type="hidden" id="target-model">
177
- <div class="model-dropdown" id="model-dropdown">
178
- <div class="model-dropdown-trigger" id="model-dropdown-trigger">
179
- <span id="model-dropdown-value">选择模型...</span>
180
- <span class="model-dropdown-arrow">&#9662;</span>
181
- </div>
182
- <div class="model-dropdown-menu" id="model-dropdown-menu">
183
- <div class="model-dropdown-options" id="model-dropdown-options"></div>
184
- <div class="model-add-section" id="model-add-section">
185
- <input type="text" class="model-add-input" id="model-add-input" placeholder="输入模型名称">
186
- <button type="button" class="btn btn-primary btn-sm" id="model-add-btn">添加</button>
187
- </div>
188
- </div>
189
- </div>
190
- </div>
191
- <div class="form-group">
192
- <label>API Key</label>
193
- <input type="password" id="target-key" placeholder="sk-...">
194
- </div>
195
- </div>
196
- <div class="form-row" id="azure-fields" style="display:none">
197
- <div class="form-group">
198
- <label>Azure Deployment <span style="color:#64748b;font-size:0.75rem">(仅 Azure 用户填写)</span></label>
199
- <input type="text" id="target-azure-deployment" placeholder="仅 Azure 用户填写">
200
- </div>
201
- <div class="form-group">
202
- <label>Azure API Version <span style="color:#64748b;font-size:0.75rem">(仅 Azure 用户填写)</span></label>
203
- <input type="text" id="target-azure-version" placeholder="仅 Azure 用户填写">
204
- </div>
205
- </div>
206
- </div>
207
- </div>
208
-
209
- <div class="modal-footer">
210
- <button type="button" class="btn" onclick="closeModal()">取消</button>
211
- <button type="submit" class="btn btn-primary">保存</button>
212
- </div>
213
- </form>
214
- </div>
215
- </div>
216
- </div>
217
-
218
- <!-- 确认弹窗 -->
219
- <div class="modal confirm-modal" id="confirm-modal">
220
- <div class="confirm-box">
221
- <div class="confirm-icon" id="confirm-icon">!</div>
222
- <p class="confirm-text" id="confirm-text"></p>
223
- <div class="confirm-actions">
224
- <button class="btn" id="confirm-cancel">取消</button>
225
- <button class="btn btn-danger" id="confirm-ok">删除</button>
226
- </div>
227
- </div>
228
- </div>
229
-
230
- <!-- 导入预览弹窗 -->
231
- <div class="modal" id="import-modal">
232
- <div class="modal-content" style="max-width:500px">
233
- <div class="modal-header">
234
- <h3>导入配置</h3>
235
- <button class="btn-close" onclick="closeImportModal()">&times;</button>
236
- </div>
237
- <div class="import-preview" id="import-preview">
238
- <div class="import-stats">
239
- <div class="import-stat">
240
- <span class="import-stat-value" id="import-providers-count">0</span>
241
- <span class="import-stat-label">供应商</span>
242
- </div>
243
- <div class="import-stat">
244
- <span class="import-stat-value" id="import-proxies-count">0</span>
245
- <span class="import-stat-label">代理</span>
246
- </div>
247
- </div>
248
- <div class="import-mode">
249
- <label>导入模式</label>
250
- <div class="import-mode-options">
251
- <label class="import-mode-option">
252
- <input type="radio" name="import-mode" value="merge" checked>
253
- <span>
254
- <strong>合并</strong>
255
- <small>按 ID 去重:新增导入项,同 ID 覆盖</small>
256
- </span>
257
- </label>
258
- <label class="import-mode-option">
259
- <input type="radio" name="import-mode" value="overwrite">
260
- <span>
261
- <strong>覆盖</strong>
262
- <small>完全替换现有配置</small>
263
- </span>
264
- </label>
265
- </div>
266
- </div>
267
- </div>
268
- <div class="modal-footer">
269
- <button class="btn" onclick="closeImportModal()">取消</button>
270
- <button class="btn btn-primary" onclick="confirmImport()">确认导入</button>
271
- </div>
272
- </div>
273
- </div>
274
-
275
- <script src="app.js"></script>
276
- </body>
277
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Protocol Proxy - 协议转换代理管理</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <header>
12
+ <h1>Protocol Proxy</h1>
13
+ <p>OpenAI / Anthropic 协议转换透明代理</p>
14
+ </header>
15
+
16
+ <div class="stats" id="stats">
17
+ <div class="stat-item">
18
+ <span class="stat-value" id="stat-total">0</span>
19
+ <span class="stat-label">代理配置</span>
20
+ </div>
21
+ <div class="stat-item">
22
+ <span class="stat-value" id="stat-running">0</span>
23
+ <span class="stat-label">运行中</span>
24
+ </div>
25
+ </div>
26
+
27
+ <!-- Token 用量统计 -->
28
+ <section class="card stats-panel">
29
+ <div class="card-header">
30
+ <h2>Token 用量统计 <span class="stats-estimated-badge" id="stats-estimated-badge" style="display:none">含估算</span></h2>
31
+ <div class="stats-controls">
32
+ <div class="model-dropdown" id="stats-proxy-dropdown">
33
+ <div class="model-dropdown-trigger stats-filter-trigger" id="stats-proxy-dropdown-trigger">
34
+ <span id="stats-proxy-dropdown-value">全部代理</span>
35
+ <span class="model-dropdown-arrow">&#9662;</span>
36
+ </div>
37
+ <div class="model-dropdown-menu" id="stats-proxy-dropdown-menu">
38
+ <div class="model-dropdown-options" id="stats-proxy-dropdown-options"></div>
39
+ </div>
40
+ </div>
41
+ <div class="stats-range-btns">
42
+ <button class="btn btn-sm stats-range-btn active" data-range="daily">每日</button>
43
+ <button class="btn btn-sm stats-range-btn" data-range="monthly">每月</button>
44
+ <button class="btn btn-sm stats-range-btn" data-range="yearly">每年</button>
45
+ <span class="stats-date-sep">|</span>
46
+ <input type="date" id="stats-start-date" class="stats-date-input" placeholder="开始日期">
47
+ <span class="stats-date-sep">~</span>
48
+ <input type="date" id="stats-end-date" class="stats-date-input" placeholder="结束日期">
49
+ </div>
50
+ </div>
51
+ </div>
52
+ <div class="stats-summary" id="stats-summary">
53
+ <div class="stats-summary-item">
54
+ <span class="stats-summary-value" id="stats-total-tokens">-</span>
55
+ <span class="stats-summary-label">总 Token</span>
56
+ </div>
57
+ <div class="stats-summary-item">
58
+ <span class="stats-summary-value" id="stats-prompt-tokens">-</span>
59
+ <span class="stats-summary-label">输入 Token</span>
60
+ </div>
61
+ <div class="stats-summary-item">
62
+ <span class="stats-summary-value" id="stats-completion-tokens">-</span>
63
+ <span class="stats-summary-label">输出 Token</span>
64
+ </div>
65
+ <div class="stats-summary-item">
66
+ <span class="stats-summary-value" id="stats-total-requests">-</span>
67
+ <span class="stats-summary-label">请求数</span>
68
+ </div>
69
+ </div>
70
+ <div id="stats-breakdown" class="stats-breakdown">
71
+ <div class="empty">暂无数据</div>
72
+ </div>
73
+ </section>
74
+
75
+ <section class="card">
76
+ <div class="card-header">
77
+ <h2>代理列表</h2>
78
+ <div class="card-header-actions">
79
+ <button class="btn" onclick="exportConfig()">导出配置</button>
80
+ <button class="btn" onclick="document.getElementById('import-file').click()">导入配置</button>
81
+ <input type="file" id="import-file" accept=".json" style="display:none" onchange="handleImportFile(event)">
82
+ <button class="btn btn-primary" onclick="openModal()">+ 新建代理</button>
83
+ </div>
84
+ </div>
85
+ <div id="proxy-list" class="proxy-list">
86
+ <div class="empty">加载中...</div>
87
+ </div>
88
+ </section>
89
+
90
+ <!-- 编辑/创建弹窗 -->
91
+ <div class="modal" id="modal">
92
+ <div class="modal-content">
93
+ <div class="modal-header">
94
+ <h3 id="modal-title">新建代理</h3>
95
+ <button class="btn-close" onclick="closeModal()">&times;</button>
96
+ </div>
97
+ <form id="proxy-form" onsubmit="handleSubmit(event)">
98
+ <input type="hidden" id="proxy-id">
99
+
100
+ <div class="form-group">
101
+ <label>代理名称</label>
102
+ <input type="text" id="proxy-name" required placeholder="例如:OpenAI 代理">
103
+ </div>
104
+
105
+ <div class="form-row">
106
+ <div class="form-group">
107
+ <label>监听端口</label>
108
+ <input type="number" id="proxy-port" required placeholder="8080" min="1000" max="65535">
109
+ </div>
110
+ <div class="form-group">
111
+ <label>Agent 认证</label>
112
+ <input type="hidden" id="proxy-auth" value="false">
113
+ <div class="model-dropdown" id="auth-dropdown">
114
+ <div class="model-dropdown-trigger" id="auth-dropdown-trigger">
115
+ <span id="auth-dropdown-value">不启用</span>
116
+ <span class="model-dropdown-arrow">&#9662;</span>
117
+ </div>
118
+ <div class="model-dropdown-menu" id="auth-dropdown-menu">
119
+ <div class="model-dropdown-options" id="auth-dropdown-options">
120
+ <div class="model-option selected" data-value="false"><span class="model-option-name">不启用</span></div>
121
+ <div class="model-option" data-value="true"><span class="model-option-name">启用</span></div>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </div>
126
+ </div>
127
+
128
+ <div class="form-group" id="auth-token-group" style="display:none">
129
+ <label>认证 Token</label>
130
+ <input type="text" id="proxy-auth-token" placeholder="Bearer Token">
131
+ </div>
132
+
133
+ <div class="target-section">
134
+ <h4>目标供应商配置</h4>
135
+ <div class="target-item">
136
+ <input type="hidden" id="provider-id">
137
+ <div class="form-row">
138
+ <div class="form-group">
139
+ <label>供应商</label>
140
+ <div class="model-dropdown" id="provider-dropdown">
141
+ <div class="model-dropdown-trigger" id="provider-dropdown-trigger">
142
+ <span id="provider-dropdown-value">选择供应商...</span>
143
+ <span class="model-dropdown-arrow">&#9662;</span>
144
+ </div>
145
+ <div class="model-dropdown-menu" id="provider-dropdown-menu">
146
+ <div class="model-dropdown-options" id="provider-dropdown-options"></div>
147
+ <div class="model-add-section">
148
+ <input type="text" class="model-add-input" id="provider-add-name" placeholder="供应商名称">
149
+ <input type="text" class="model-add-input" id="provider-add-url" placeholder="https://api.example.com">
150
+ <button type="button" class="btn btn-primary btn-sm" id="provider-add-btn">添加</button>
151
+ </div>
152
+ </div>
153
+ </div>
154
+ </div>
155
+ <div class="form-group">
156
+ <label>协议</label>
157
+ <input type="hidden" id="target-protocol" value="openai">
158
+ <div class="model-dropdown" id="protocol-dropdown">
159
+ <div class="model-dropdown-trigger" id="protocol-dropdown-trigger">
160
+ <span id="protocol-dropdown-value">OpenAI</span>
161
+ <span class="model-dropdown-arrow">&#9662;</span>
162
+ </div>
163
+ <div class="model-dropdown-menu" id="protocol-dropdown-menu">
164
+ <div class="model-dropdown-options" id="protocol-dropdown-options">
165
+ <div class="model-option selected" data-value="openai"><span class="model-option-name">OpenAI</span></div>
166
+ <div class="model-option" data-value="anthropic"><span class="model-option-name">Anthropic</span></div>
167
+ <div class="model-option" data-value="gemini"><span class="model-option-name">Gemini</span></div>
168
+ </div>
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ <div class="form-row">
174
+ <div class="form-group">
175
+ <label>默认 Model(可选)</label>
176
+ <input type="hidden" id="target-model">
177
+ <div class="model-dropdown" id="model-dropdown">
178
+ <div class="model-dropdown-trigger" id="model-dropdown-trigger">
179
+ <span id="model-dropdown-value">选择模型...</span>
180
+ <span class="model-dropdown-arrow">&#9662;</span>
181
+ </div>
182
+ <div class="model-dropdown-menu" id="model-dropdown-menu">
183
+ <div class="model-dropdown-options" id="model-dropdown-options"></div>
184
+ <div class="model-add-section" id="model-add-section">
185
+ <input type="text" class="model-add-input" id="model-add-input" placeholder="输入模型名称">
186
+ <button type="button" class="btn btn-primary btn-sm" id="model-add-btn">添加</button>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ <div class="form-row api-keys-row">
193
+ <div class="form-group">
194
+ <div class="api-keys-header">
195
+ <span>API Keys</span>
196
+ <button type="button" id="api-key-add-btn" class="btn btn-sm">+ 添加 Key</button>
197
+ </div>
198
+ <div id="api-keys-list"></div>
199
+ </div>
200
+ </div>
201
+ <div class="form-row">
202
+ <div class="form-group">
203
+ <label>路由策略</label>
204
+ <input type="hidden" id="routing-strategy" value="primary_fallback">
205
+ <div class="model-dropdown" id="routing-dropdown">
206
+ <div class="model-dropdown-trigger" id="routing-dropdown-trigger">
207
+ <span id="routing-dropdown-value">主备切换</span>
208
+ <span class="model-dropdown-arrow">&#9662;</span>
209
+ </div>
210
+ <div class="model-dropdown-menu" id="routing-dropdown-menu">
211
+ <div class="model-dropdown-options" id="routing-dropdown-options">
212
+ <div class="model-option selected" data-value="primary_fallback"><span class="model-option-name">主备切换</span></div>
213
+ <div class="model-option" data-value="round_robin"><span class="model-option-name">轮询</span></div>
214
+ <div class="model-option" data-value="weighted"><span class="model-option-name">加权</span></div>
215
+ <div class="model-option" data-value="fastest"><span class="model-option-name">最快优先</span></div>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ <div class="form-group" style="max-width:120px">
221
+ <label>主供应商权重</label>
222
+ <input type="number" id="provider-weight" min="1" step="1" value="1" placeholder="1">
223
+ </div>
224
+ <div class="form-group provider-pool-group">
225
+ <label>备选供应商</label>
226
+ <div class="provider-pool-picker">
227
+ <div class="model-dropdown" id="provider-pool-dropdown">
228
+ <div class="model-dropdown-trigger" id="provider-pool-dropdown-trigger">
229
+ <span id="provider-pool-dropdown-value">从供应商列表添加</span>
230
+ <span class="model-dropdown-arrow">&#9662;</span>
231
+ </div>
232
+ <div class="model-dropdown-menu" id="provider-pool-dropdown-menu">
233
+ <div class="model-dropdown-options" id="provider-pool-dropdown-options"></div>
234
+ </div>
235
+ </div>
236
+ </div>
237
+ <div class="provider-pool-list" id="provider-pool-list"></div>
238
+ </div>
239
+ </div>
240
+ <div class="form-row" id="azure-fields" style="display:none">
241
+ <div class="form-group">
242
+ <label>Azure Deployment <span style="color:#64748b;font-size:0.75rem">(仅 Azure 用户填写)</span></label>
243
+ <input type="text" id="target-azure-deployment" placeholder="仅 Azure 用户填写">
244
+ </div>
245
+ <div class="form-group">
246
+ <label>Azure API Version <span style="color:#64748b;font-size:0.75rem">(仅 Azure 用户填写)</span></label>
247
+ <input type="text" id="target-azure-version" placeholder="仅 Azure 用户填写">
248
+ </div>
249
+ </div>
250
+ </div>
251
+ </div>
252
+
253
+ <div class="modal-footer">
254
+ <button type="button" class="btn" onclick="closeModal()">取消</button>
255
+ <button type="submit" class="btn btn-primary">保存</button>
256
+ </div>
257
+ </form>
258
+ </div>
259
+ </div>
260
+ </div>
261
+
262
+ <!-- 确认弹窗 -->
263
+ <div class="modal confirm-modal" id="confirm-modal">
264
+ <div class="confirm-box">
265
+ <div class="confirm-icon" id="confirm-icon">!</div>
266
+ <p class="confirm-text" id="confirm-text"></p>
267
+ <div class="confirm-actions">
268
+ <button class="btn" id="confirm-cancel">取消</button>
269
+ <button class="btn btn-danger" id="confirm-ok">删除</button>
270
+ </div>
271
+ </div>
272
+ </div>
273
+
274
+ <!-- 导入预览弹窗 -->
275
+ <div class="modal" id="import-modal">
276
+ <div class="modal-content" style="max-width:500px">
277
+ <div class="modal-header">
278
+ <h3>导入配置</h3>
279
+ <button class="btn-close" onclick="closeImportModal()">&times;</button>
280
+ </div>
281
+ <div class="import-preview" id="import-preview">
282
+ <div class="import-stats">
283
+ <div class="import-stat">
284
+ <span class="import-stat-value" id="import-providers-count">0</span>
285
+ <span class="import-stat-label">供应商</span>
286
+ </div>
287
+ <div class="import-stat">
288
+ <span class="import-stat-value" id="import-proxies-count">0</span>
289
+ <span class="import-stat-label">代理</span>
290
+ </div>
291
+ </div>
292
+ <div class="import-mode">
293
+ <label>导入模式</label>
294
+ <div class="import-mode-options">
295
+ <label class="import-mode-option">
296
+ <input type="radio" name="import-mode" value="merge" checked>
297
+ <span>
298
+ <strong>合并</strong>
299
+ <small>按 ID 去重:新增导入项,同 ID 覆盖</small>
300
+ </span>
301
+ </label>
302
+ <label class="import-mode-option">
303
+ <input type="radio" name="import-mode" value="overwrite">
304
+ <span>
305
+ <strong>覆盖</strong>
306
+ <small>完全替换现有配置</small>
307
+ </span>
308
+ </label>
309
+ </div>
310
+ </div>
311
+ </div>
312
+ <div class="modal-footer">
313
+ <button class="btn" onclick="closeImportModal()">取消</button>
314
+ <button class="btn btn-primary" onclick="confirmImport()">确认导入</button>
315
+ </div>
316
+ </div>
317
+ </div>
318
+
319
+ <script src="app.js"></script>
320
+ </body>
321
+ </html>