merge: 合入 test(KOC资源卡片手动修改账号ID)

This commit is contained in:
ABAPPLO
2026-08-27 17:57:04 +08:00
2 changed files with 137 additions and 0 deletions

View File

@@ -3196,6 +3196,10 @@ function ResourcesPage({
}); });
const [singleWorking, setSingleWorking] = useState(false); const [singleWorking, setSingleWorking] = useState(false);
const [singleError, setSingleError] = useState(""); const [singleError, setSingleError] = useState("");
const [editAccount, setEditAccount] = useState<Account | null>(null);
const [editAccountId, setEditAccountId] = useState("");
const [editWorking, setEditWorking] = useState(false);
const [editError, setEditError] = useState("");
const resourceAccounts = useMemo(() => { const resourceAccounts = useMemo(() => {
const deliveriesByAccount = new Map<string, Distribution[]>(); const deliveriesByAccount = new Map<string, Distribution[]>();
distributions.forEach((distribution) => { distributions.forEach((distribution) => {
@@ -3403,6 +3407,46 @@ function ResourcesPage({
setImportWorking(false); setImportWorking(false);
} }
}; };
const closeAccountEdit = () => {
if (editWorking) return;
setEditAccount(null);
setEditAccountId("");
setEditError("");
};
const submitAccountEdit = async () => {
if (!editAccount) return;
const value = editAccountId.trim();
if (!value) {
setEditError("请填写新的账号ID");
return;
}
if (!/^[\p{L}\p{N}._-]{2,80}$/u.test(value)) {
setEditError("账号ID需为 2-80 位字母、数字或 . _ - 组合");
return;
}
try {
setEditWorking(true);
setEditError("");
const response = await fetch("/api/action", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: "set_public_account_ids",
items: [{ accountId: editAccount.id, publicAccountId: value }],
}),
});
const result = await readApiResponse<{ error?: string }>(response, "保存失败");
if (!response.ok) throw new Error(result.error || "保存失败");
const label = editAccount.nickname || editAccount.public_account_id || "该账号";
setEditAccount(null);
setEditAccountId("");
await onImported(`已更新 ${label} 的账号ID`);
} catch (reason) {
setEditError(reason instanceof Error ? reason.message : "保存失败");
} finally {
setEditWorking(false);
}
};
return ( return (
<div className="stack"> <div className="stack">
<section className="resource-intro"> <section className="resource-intro">
@@ -3548,6 +3592,18 @@ function ResourcesPage({
<div className="resource-account-number"> <div className="resource-account-number">
<span>{accountIdLabel}</span> <span>{accountIdLabel}</span>
<strong>{account.public_account_id || "待识别"}</strong> <strong>{account.public_account_id || "待识别"}</strong>
<button
type="button"
className="account-id-edit-button"
title="手动修改账号ID"
onClick={() => {
setEditAccount(account);
setEditAccountId(account.public_account_id || "");
setEditError("");
}}
>
修改
</button>
</div> </div>
<div className="resource-platform-line"> <div className="resource-platform-line">
<PlatformBadge platform={account.platform} compact /> <PlatformBadge platform={account.platform} compact />
@@ -3601,6 +3657,59 @@ function ResourcesPage({
</div> </div>
)} )}
</section> </section>
{editAccount && (
<div className="modal-backdrop" role="presentation" onMouseDown={closeAccountEdit}>
<div
className="modal-card compact"
role="dialog"
aria-modal="true"
aria-labelledby="account-id-edit-title"
onMouseDown={(event) => event.stopPropagation()}
>
<div className="modal-heading">
<div>
<p className="eyebrow">{editAccount.nickname || "账号"}</p>
<h2 id="account-id-edit-title">修改账号ID</h2>
</div>
<button type="button" onClick={closeAccountEdit} aria-label="关闭">×</button>
</div>
<div className="account-id-edit-form">
<label>
<span>
{editAccount.platform === "小红书"
? "小红书号"
: editAccount.platform === "抖音"
? "抖音号"
: "账号ID"}
</span>
<input
autoFocus
type="text"
value={editAccountId}
placeholder="2-80 位字母、数字或 . _ -"
onChange={(event) => setEditAccountId(event.target.value)}
onKeyDown={(event) => {
if (event.key === "Enter") void submitAccountEdit();
}}
/>
<small>只更新账号ID,昵称、粉丝数等其他资料保持不变</small>
</label>
{editError && <div className="form-error">{editError}</div>}
</div>
<div className="modal-actions">
<button type="button" className="ghost-button" onClick={closeAccountEdit}>取消</button>
<button
type="button"
className="primary-button"
disabled={editWorking || !editAccountId.trim()}
onClick={() => void submitAccountEdit()}
>
{editWorking ? "保存中…" : "保存"}
</button>
</div>
</div>
</div>
)}
{importOpen && ( {importOpen && (
<div className="modal-backdrop" role="presentation" onMouseDown={closeImport}> <div className="modal-backdrop" role="presentation" onMouseDown={closeImport}>
<div <div

View File

@@ -2972,6 +2972,34 @@ a {
white-space: nowrap; white-space: nowrap;
} }
.account-id-edit-button {
flex: 0 0 auto;
padding: 1px 6px;
border: 1px solid #dce2de;
border-radius: 999px;
color: #8a978f;
background: transparent;
font-size: 9px;
line-height: 1.5;
cursor: pointer;
}
.account-id-edit-button:hover {
border-color: #70ad95;
color: #40806a;
}
.account-id-edit-form {
display: flex;
flex-direction: column;
gap: 4px;
}
.account-id-edit-form small {
color: #9aa4a0;
font-size: 10px;
}
.resource-platform-line { .resource-platform-line {
display: flex; display: flex;
min-width: 0; min-width: 0;