新增设备别名管理功能,支持设备别名的保存与解绑
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { startTransition, useState } from "react";
|
||||
import { startTransition, useEffect, useState } from "react";
|
||||
|
||||
export function BindDeviceForm() {
|
||||
const router = useRouter();
|
||||
@@ -196,4 +196,158 @@ export function SendMessageForm({ deviceUuid }: SendMessageFormProps) {
|
||||
) : null}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
type ManageDeviceFormProps = {
|
||||
deviceUuid: string;
|
||||
initialAlias?: string | null;
|
||||
deviceLabel: string;
|
||||
};
|
||||
|
||||
export function ManageDeviceForm({
|
||||
deviceUuid,
|
||||
initialAlias,
|
||||
deviceLabel,
|
||||
}: ManageDeviceFormProps) {
|
||||
const router = useRouter();
|
||||
const [alias, setAlias] = useState(initialAlias ?? "");
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [unbinding, setUnbinding] = useState(false);
|
||||
const [notice, setNotice] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setAlias(initialAlias ?? "");
|
||||
}, [initialAlias]);
|
||||
|
||||
async function handleSaveAlias(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
setSaving(true);
|
||||
setNotice("正在保存设备别名……");
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/family/device-binding", {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
deviceUuid,
|
||||
alias,
|
||||
}),
|
||||
});
|
||||
|
||||
const payload = (await response.json().catch(() => null)) as
|
||||
| { error?: string; alias?: string | null; deviceName?: string }
|
||||
| null;
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(payload?.error || "设备别名保存失败。");
|
||||
}
|
||||
|
||||
setAlias(payload?.alias?.trim() || "");
|
||||
setNotice(
|
||||
payload?.alias?.trim()
|
||||
? `已将这台设备备注为“${payload.alias.trim()}”。\n`
|
||||
: "已恢复默认设备名称。\n",
|
||||
);
|
||||
startTransition(() => {
|
||||
router.refresh();
|
||||
});
|
||||
} catch (error) {
|
||||
setNotice(
|
||||
error instanceof Error ? error.message : "设备别名保存失败。",
|
||||
);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleUnbind() {
|
||||
if (!window.confirm(`解绑后将不再收到“${deviceLabel}”的留言和推送提醒,确定继续吗?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setUnbinding(true);
|
||||
setNotice("正在解绑这台设备……");
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/family/device-binding", {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
deviceUuid,
|
||||
}),
|
||||
});
|
||||
|
||||
const payload = (await response.json().catch(() => null)) as
|
||||
| { error?: string }
|
||||
| null;
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(payload?.error || "解绑设备失败。",);
|
||||
}
|
||||
|
||||
startTransition(() => {
|
||||
router.push("/devices");
|
||||
router.refresh();
|
||||
});
|
||||
} catch (error) {
|
||||
setNotice(
|
||||
error instanceof Error ? error.message : "解绑设备失败。",
|
||||
);
|
||||
setUnbinding(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="rounded-[28px] border border-white/70 bg-white/88 p-5">
|
||||
<p className="text-sm font-medium tracking-[0.18em] text-[var(--copper)] uppercase">
|
||||
设备管理
|
||||
</p>
|
||||
<h4 className="mt-2 font-[family-name:var(--font-display)] text-xl text-[var(--ink)]">
|
||||
修改别名或解绑设备
|
||||
</h4>
|
||||
<p className="mt-2 text-sm leading-7 text-[var(--muted)]">
|
||||
设备别名会显示在设备列表、留言页和推送通知里,例如“外婆的手机”。
|
||||
</p>
|
||||
|
||||
<form className="mt-4 flex flex-col gap-3 sm:flex-row" onSubmit={handleSaveAlias}>
|
||||
<input
|
||||
value={alias}
|
||||
onChange={(event) => setAlias(event.target.value)}
|
||||
maxLength={24}
|
||||
placeholder="给这台设备起个好记的名字"
|
||||
className="h-12 flex-1 rounded-full border border-[var(--line)] bg-[var(--paper-soft)] px-5 text-sm text-[var(--ink)] outline-none transition focus:border-[var(--copper)] focus:bg-white"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={saving || unbinding}
|
||||
className="inline-flex h-12 min-w-32 items-center justify-center rounded-full bg-[var(--olive)] px-5 text-sm font-semibold text-white transition hover:bg-[var(--olive-deep)] disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
{saving ? "正在保存" : "保存别名"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="mt-4 flex flex-wrap gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void handleUnbind();
|
||||
}}
|
||||
disabled={saving || unbinding}
|
||||
className="inline-flex h-11 items-center justify-center rounded-full border border-[rgba(160,74,48,0.28)] bg-white px-4 text-sm font-semibold text-[var(--ink)] transition hover:bg-[rgba(199,103,51,0.08)] disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
{unbinding ? "正在解绑" : "解绑这台设备"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{notice ? (
|
||||
<p className="mt-3 whitespace-pre-line text-sm leading-7 text-[var(--muted)]">
|
||||
{notice}
|
||||
</p>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user