From d926591ace8847ed332ec343867730b54a9e6b4d Mon Sep 17 00:00:00 2001 From: lanrtop Date: Mon, 28 Sep 2026 01:45:49 +0900 Subject: [PATCH] =?UTF-8?q?feat(dev-variables/ui):=20=E4=B8=80=E5=BC=A0?= =?UTF-8?q?=E5=8D=A1=E5=A1=AB=E5=86=99=E6=B5=8B=E8=AF=95=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E5=92=8C=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 项目面板一次提交两个字段到同一 Tauri 本体,列表与 React Query 仅保留非秘密状态。 Rules-Applied: R01,R08 Feature-Confirmed: true --- src-tauri/src/lib.rs | 1 + .../dashboard/ProjectDevVariablesPanel.tsx | 89 ++++++++++++++----- src/lib/commands.ts | 10 +++ 3 files changed, 76 insertions(+), 24 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 38e764d..4a91480 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -405,6 +405,7 @@ pub fn run() { // project development variables (human filling; no reveal command) commands::dev_variables::list_project_dev_variables, commands::dev_variables::save_project_dev_variable, + commands::dev_variables::save_project_test_account, commands::dev_variables::revoke_project_dev_variable, // host apps commands::host_apps::get_host_apps, diff --git a/src/components/dashboard/ProjectDevVariablesPanel.tsx b/src/components/dashboard/ProjectDevVariablesPanel.tsx index 5056397..0dacf0d 100644 --- a/src/components/dashboard/ProjectDevVariablesPanel.tsx +++ b/src/components/dashboard/ProjectDevVariablesPanel.tsx @@ -3,6 +3,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { listProjectDevVariables, saveProjectDevVariable, + saveProjectTestAccount, revokeProjectDevVariable, } from "../../lib/commands"; import { useUIStore } from "../../store/ui"; @@ -19,21 +20,32 @@ export function ProjectDevVariablesPanel({ projectId, projectName, onClose }: Pr const showToast = useUIStore((state) => state.showToast); const [editingName, setEditingName] = useState(null); const [draftValue, setDraftValue] = useState(""); + const [draftUsername, setDraftUsername] = useState(""); + const [draftPassword, setDraftPassword] = useState(""); const [saving, setSaving] = useState(false); const { data: variables = [], isPending, error } = useQuery({ queryKey: ["projectDevVariables", projectId], queryFn: () => listProjectDevVariables(projectId), retry: false, }); - const handleSave = async (name: string) => { - if (!draftValue || saving) return; + const clearDraft = () => { + setDraftValue(""); + setDraftUsername(""); + setDraftPassword(""); + }; + const handleSave = async (name: string, kind: "scalar" | "test_account") => { + if (saving || (kind === "test_account" ? !draftUsername || !draftPassword : !draftValue)) return; setSaving(true); try { - await saveProjectDevVariable(projectId, name, draftValue); - setDraftValue(""); + if (kind === "test_account") { + await saveProjectTestAccount(projectId, name, draftUsername, draftPassword); + } else { + await saveProjectDevVariable(projectId, name, draftValue); + } + clearDraft(); setEditingName(null); queryClient.invalidateQueries({ queryKey: ["projectDevVariables", projectId] }); - showToast("开发变量已保存"); + showToast(kind === "test_account" ? "测试账户已保存" : "开发变量已保存"); } catch (cause) { showToast(`保存失败:${cause}`); } finally { @@ -53,7 +65,7 @@ export function ProjectDevVariablesPanel({ projectId, projectName, onClose }: Pr

- 仅填写非生产、低权限、可重置的开发值。已配置的值可被此项目的已认证 Agent 单项读取, + 仅填写非生产、低权限、可重置的开发值或测试账户。已配置的值可被此项目的已认证 Agent 单项读取, 不会写入子项目文件;请勿存放生产密码。

{isPending &&

正在读取需求…

} @@ -69,34 +81,63 @@ export function ProjectDevVariablesPanel({ projectId, projectName, onClose }: Pr {item.configured ? "已配置" : "待填写"}
+ {item.kind === "test_account" && ( +

测试账户 · {item.service} · {item.environment}

+ )}

{item.purpose}

{editingName === item.name ? (
{ event.preventDefault(); - void handleSave(item.name); + void handleSave(item.name, item.kind); }} - className="flex flex-wrap gap-2" + className="space-y-2" > - setDraftValue(event.target.value)} - placeholder="填写开发值" - className="flex-1 min-w-44 border border-gray-300 rounded px-2 py-1.5 text-sm" - /> - - + {item.kind === "test_account" ? ( +
+ setDraftUsername(event.target.value)} + placeholder="测试账号" + aria-label={`${item.name} 测试账号`} + className="w-full border border-gray-300 rounded px-2 py-1.5 text-sm" + /> + setDraftPassword(event.target.value)} + placeholder="测试密码" + aria-label={`${item.name} 测试密码`} + className="w-full border border-gray-300 rounded px-2 py-1.5 text-sm" + /> +
+ ) : ( + setDraftValue(event.target.value)} + placeholder="填写开发值" + className="w-full border border-gray-300 rounded px-2 py-1.5 text-sm" + /> + )} +
+ + +
) : (
- {item.configured && (