diff --git a/frontend/src/components/pages/AssistantPage.tsx b/frontend/src/components/pages/AssistantPage.tsx index 578e81a..3ae5572 100644 --- a/frontend/src/components/pages/AssistantPage.tsx +++ b/frontend/src/components/pages/AssistantPage.tsx @@ -7,6 +7,8 @@ import { Check, Copy, Database, + Eye, + EyeOff, MessageSquareText, MoreHorizontal, Pencil, @@ -525,7 +527,8 @@ export function AssistantPage() { setDifyForm({ name: a.name, apiUrl: a.apiUrl, - apiKey: a.apiKey, // 编辑时为打码值,不改则原样回传(后端哨兵保留旧 key) + // 编辑时不把打码占位符放入输入框;空值写回后端表示保留旧 key + apiKey: "", asr: a.asrCredentialId ?? "", voice: a.ttsCredentialId ?? "", enableInterrupt: a.enableInterrupt, @@ -552,7 +555,8 @@ export function AssistantPage() { name: a.name, appId: a.appId, apiUrl: a.apiUrl, - apiKey: a.apiKey, + // 编辑时不把打码占位符放入输入框;空值写回后端表示保留旧 key + apiKey: "", asr: a.asrCredentialId ?? "", voice: a.ttsCredentialId ?? "", enableInterrupt: a.enableInterrupt, @@ -580,6 +584,9 @@ export function AssistantPage() { type: typeToLabel[a.type], updatedAt: formatTimestamp(a.updatedAt), })); + const hasStoredApiKey = Boolean( + editingId && assistants.find((assistant) => assistant.id === editingId)?.apiKey, + ); const filteredAssistants = listItems.filter((assistant) => { if (typeFilter !== "全部" && assistant.type !== typeFilter) { @@ -1082,6 +1089,7 @@ export function AssistantPage() {
+ setView("list")} /> updateDifyForm("name", value)} @@ -1089,15 +1097,6 @@ export function AssistantPage() {
- - {saveError && ( {saveError} @@ -1131,12 +1130,12 @@ export function AssistantPage() { onChange={(value) => updateDifyForm("apiUrl", value)} placeholder="https://api.dify.ai/v1/chat-messages" /> - updateDifyForm("apiKey", value)} placeholder="请输入 Dify API Key" - type="password" + hasStoredValue={hasStoredApiKey} /> @@ -1188,6 +1187,7 @@ export function AssistantPage() {
+ setView("list")} /> updateFastGptForm("name", value)} @@ -1195,15 +1195,6 @@ export function AssistantPage() {
- - {saveError && ( {saveError} @@ -1243,12 +1234,12 @@ export function AssistantPage() { onChange={(value) => updateFastGptForm("apiUrl", value)} placeholder="https://api.fastgpt.in/api/v1/chat/completions" /> - updateFastGptForm("apiKey", value)} placeholder="请输入 FastGPT API Key" - type="password" + hasStoredValue={hasStoredApiKey} /> @@ -1300,6 +1291,7 @@ export function AssistantPage() {
+ setView("list")} /> updateOpenCodeForm("name", value)} @@ -1307,15 +1299,6 @@ export function AssistantPage() {
- - - {saveError && ( {saveError} @@ -1754,6 +1729,20 @@ function DebugVoicePanel() { ); } +function EditorBackButton({ onClick }: { onClick: () => void }) { + return ( + + ); +} + function EditableTitle({ value, onChange, @@ -1915,6 +1904,60 @@ function InputField({ ); } +function SecretInputField({ + label, + value, + placeholder, + hasStoredValue, + onChange, +}: { + label?: string; + value: string; + placeholder?: string; + hasStoredValue: boolean; + onChange: (value: string) => void; +}) { + const [showValue, setShowValue] = useState(false); + + return ( + + ); +} + function TextAreaField({ label, value, diff --git a/frontend/src/components/pages/ComponentsModelsPage.tsx b/frontend/src/components/pages/ComponentsModelsPage.tsx index 3bc793e..4ca1480 100644 --- a/frontend/src/components/pages/ComponentsModelsPage.tsx +++ b/frontend/src/components/pages/ComponentsModelsPage.tsx @@ -145,7 +145,9 @@ export function ComponentsModelsPage() { try { // TODO: 接入真实疎通接口(按 form.interfaceType 区分调用方式) await new Promise((resolve) => setTimeout(resolve, 900)); - const reachable = Boolean(form.apiUrl.trim() && form.apiKey.trim()); + const reachable = Boolean( + form.apiUrl.trim() && (form.apiKey.trim() || editingModel?.apiKey), + ); setTestResult(reachable ? "ok" : "fail"); } catch { setTestResult("fail"); @@ -186,8 +188,8 @@ export function ComponentsModelsPage() { type: model.type, interfaceType: model.interfaceType, apiUrl: model.apiUrl, - // 后端回传已打码,原样带回 → 不修改则保留旧 key(写时哨兵) - apiKey: model.apiKey, + // 编辑时不把打码占位符放入输入框;空值写回后端表示保留旧 key + apiKey: "", }); setShowKey(false); setTestResult("idle"); @@ -282,6 +284,7 @@ export function ComponentsModelsPage() { } const interfaceOptions = interfaceOptionsByType[form.type]; + const hasStoredApiKey = Boolean(editingId && editingModel?.apiKey); const canSave = form.name.trim() && form.modelId.trim() && form.apiUrl.trim(); @@ -699,18 +702,29 @@ export function ComponentsModelsPage() { value={form.apiKey} type={showKey ? "text" : "password"} onChange={(event) => updateForm("apiKey", event.target.value)} - placeholder="请输入 API Key" + placeholder={ + hasStoredApiKey + ? "已配置,留空则保持不变" + : "请输入 API Key" + } + autoComplete="new-password" className="border-hairline-strong bg-background pr-10 text-foreground placeholder:text-muted-soft" />
+ {hasStoredApiKey && ( +

+ 已有密钥不会返回浏览器。留空可保持原密钥,输入新值将覆盖原密钥。 +

+ )}