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 */
913import { useEffect , useState } from "react" ;
1014import { 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 ) } />
0 commit comments