Skip to content

Commit 7589656

Browse files
committed
fix(ai): Provider 面板确认弹窗与内联密钥输入(v0.11.6 M1 审查修复)
1 parent bdbbb5f commit 7589656

2 files changed

Lines changed: 54 additions & 11 deletions

File tree

‎app/src/components/AiProviderSettings.tsx‎

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
* 启用/删除/设默认)+ "添加 Provider"向导(预设模板:SiliconFlow/
66
* DeepSeek/OpenRouter/Ollama/自定义)。密钥只写不回传——视图仅
77
* 显示"已配置";测试连接走 ai_provider_test(最小 chat 请求)。
8+
*
9+
* @line-limit-exemption: 338 行(300 上限)——v0.11.6 M1 code-review 修复
10+
* (确认弹窗/内联密钥输入/textarea 模型列表)后越线,
11+
* 已登记 docs/standards/line-limit-exemptions.md。
812
*/
913
import { useEffect, useState } from "react";
1014
import { invoke } from "@tauri-apps/api/core";
@@ -42,6 +46,8 @@ export default function AiProviderSettings() {
4246
const [busy, setBusy] = useState(false);
4347
const [editing, setEditing] = useState<AiProviderView | null>(null);
4448
const [showAdd, setShowAdd] = useState(false);
49+
const [keyInputId, setKeyInputId] = useState<string | null>(null);
50+
const [keyInput, setKeyInput] = useState("");
4551

4652
useEffect(() => {
4753
void load();
@@ -58,6 +64,7 @@ export default function AiProviderSettings() {
5864

5965
const run = async (op: () => Promise<unknown>, okText?: string) => {
6066
setBusy(true);
67+
setMsg({ kind: "ok", text: "处理中…" });
6168
try {
6269
await op();
6370
if (okText) setMsg({ kind: "ok", text: okText });
@@ -75,8 +82,10 @@ export default function AiProviderSettings() {
7582
setMsg({ kind: "ok", text: `连接成功:${reply}` });
7683
});
7784

78-
const removeProvider = (id: string) =>
85+
const removeProvider = (id: string) => {
86+
if (!window.confirm("删除后该 Provider 配置与密钥将永久清除,且不可恢复。确定删除?")) return;
7987
run(() => invoke("ai_provider_remove", { id }), "已删除");
88+
};
8089

8190
const setDefault = (id: string) =>
8291
run(() => invoke("ai_set_default_provider", { id }), "已设为默认");
@@ -99,14 +108,10 @@ export default function AiProviderSettings() {
99108
p.enabled ? "已禁用" : "已启用",
100109
);
101110

102-
const saveKey = (id: string) => {
103-
const key = window.prompt("输入 API 密钥(将加密保存到系统凭据保护)");
104-
if (key === null) return;
105-
run(() => invoke("ai_provider_save_key", { id, apiKey: key.trim() }), "密钥已保存");
106-
};
107-
108-
const clearKey = (id: string) =>
111+
const clearKey = (id: string) => {
112+
if (!window.confirm("清除后密钥不可恢复(需重新输入)。确定清除?")) return;
109113
run(() => invoke("ai_provider_clear_key", { id }), "密钥已清除");
114+
};
110115

111116
return (
112117
<div style={{ fontSize: 12, color: "#1f2937" }}>
@@ -142,12 +147,43 @@ export default function AiProviderSettings() {
142147
{p.hasKey ? (
143148
<button style={btn} onClick={() => void clearKey(p.id)} disabled={busy}>清除密钥</button>
144149
) : (
145-
<button style={btn} onClick={() => void saveKey(p.id)} disabled={busy}>配置密钥</button>
150+
<button style={btn} onClick={() => setKeyInputId(p.id)} disabled={busy}>配置密钥</button>
146151
)}
147152
{!p.isDefault && (
148153
<button style={{ ...btn, color: "#dc2626" }} onClick={() => void removeProvider(p.id)} disabled={busy}>删除</button>
149154
)}
150155
</div>
156+
{keyInputId === p.id && (
157+
<div style={{ display: "flex", gap: 6, marginTop: 6 }}>
158+
<input
159+
style={inputStyle}
160+
type="password"
161+
placeholder="API 密钥"
162+
value={keyInput}
163+
onChange={(e) => setKeyInput(e.target.value)}
164+
/>
165+
<button
166+
style={{ ...btn, background: "#0d9488", color: "#fff", border: "none" }}
167+
disabled={busy}
168+
onClick={() => {
169+
void run(() => invoke("ai_provider_save_key", { id: p.id, apiKey: keyInput.trim() }), "密钥已保存");
170+
setKeyInput("");
171+
setKeyInputId(null);
172+
}}
173+
>
174+
保存
175+
</button>
176+
<button
177+
style={btn}
178+
onClick={() => {
179+
setKeyInputId(null);
180+
setKeyInput("");
181+
}}
182+
>
183+
取消
184+
</button>
185+
</div>
186+
)}
151187
</div>
152188
))}
153189

@@ -230,7 +266,7 @@ function ProviderForm({
230266
models: modelList,
231267
defaultModel: defaultModel.trim(),
232268
enabled: initial?.enabled ?? true,
233-
fallbackOrder: [],
269+
fallbackOrder: initial?.fallbackOrder ?? [],
234270
apiKey: apiKey.trim() || undefined,
235271
};
236272
setBusy(true);
@@ -271,7 +307,13 @@ function ProviderForm({
271307
<input style={{ ...inputStyle, width: 160 }} placeholder="端点 URL" value={baseUrl} onChange={(e) => setBaseUrl(e.target.value)} />
272308
</div>
273309
<div style={{ marginBottom: 6 }}>
274-
<input style={inputStyle} placeholder="模型列表(每行一个)" value={models} onChange={(e) => setModels(e.target.value)} />
310+
<textarea
311+
style={{ ...inputStyle, resize: "vertical" }}
312+
rows={2}
313+
placeholder="模型列表(每行一个)"
314+
value={models}
315+
onChange={(e) => setModels(e.target.value)}
316+
/>
275317
</div>
276318
<div style={{ display: "flex", gap: 6, marginBottom: 6 }}>
277319
<input style={inputStyle} placeholder="默认模型" value={defaultModel} onChange={(e) => setDefaultModel(e.target.value)} />

‎docs/standards/line-limit-exemptions.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
| app/src/components/ProfileDetector.tsx | 330 | 档案检测组件:投票/确认流/记忆偏好 UI + v0.11.5 Task 5 冲突提示内聚(实测 2026-08-22) | 若再增长:确认流拆至 ProfileConfirmFlow.tsx |
6868
| app/src/components/NoteEditView.tsx | 315 | 笔记编辑视图:编辑态/标签/结构面板内聚(前端审查登记) | 若再增长:结构面板拆至 NoteStructurePane.tsx |
6969
| app/src/components/NoteGroupPanel.tsx | 545 | v0.11.0~4 笔记组侧栏:组列表/路由可见可改/碎片捕获/闪卡生成与复习入口/结算仪式/周契约卡/feed 碎片列表(周契约与碎片列表已拆独立组件 WeekContractCard/FeedFragmentList)——组域交互单一面板内聚;v0.11.5 树形合并(buildTree 消费/搜索新建工具条/受控展开组) | 若再增长:结算仪式区拆至 GroupSettlementPane.tsx |
70+
| app/src/components/AiProviderSettings.tsx | 338 | v0.11.6 M1 code-review 修复(2026-08-22):删除/清钥加 window.confirm、window.prompt 改卡片内联 password 输入(+2 state + 内联表单)、模型列表 input 改 textarea、fallbackOrder 透传 initial、run 置"处理中"反馈——修复净增约 41 行越线(实测 338,含 4 行豁免头注释) | 若再增长:内联密钥表单拆至 AiProviderKeyInput.tsx |
7071

7172
> 前端 **拆分中**(Task #9 笔记域修复进行中,暂不登记行数):`app/src/types.ts`、`app/src/pages/NotesPage.tsx`——待前端拆分完成后以实测行数重新评估。
7273
> 前端 SessionsPage.tsx 审查快照 304 行(登记值),v0.7.1 硬拆后长期 ≤300,本轮审查期间轻微越线;随 NotesPage/types.ts 拆分任务一并复核,若仍越线按上表模式登记。

0 commit comments

Comments
 (0)