From 21c4701de9fa688c1e0f7c9a60bdc685aff3a3c2 Mon Sep 17 00:00:00 2001 From: Pine Date: Fri, 28 Aug 2026 17:26:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(login):=20=E7=99=BB=E5=BD=95=E6=94=B9?= =?UTF-8?q?=E9=80=A0(=E5=8E=BB=E8=B4=A6=E5=AF=86/=E7=9F=AD=E4=BF=A1+?= =?UTF-8?q?=E6=89=AB=E7=A0=81)=20+=20=E8=B5=84=E8=AE=AF=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E6=97=B6=E9=97=B4/=E6=9D=A5=E6=BA=90/=E5=AF=BC=E8=AF=BB?= =?UTF-8?q?=E8=A1=A8=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 登录页移除账号密码 tab,仅短信+小程序扫码;扫码组件重写:倒计时+失效模糊蒙版+手动刷新(失效即停轮询,绝不自动刷新) - 文章编辑器:发布时间(datetime-local,定时发布)+来源+导读(独立编辑不限字数)+作者留空显示官方 - 园区端发布:来源+发布时间(禁早于3天前校验),无发布人输入 --- src/pages/article-editor/index.tsx | 60 ++++++++++-- src/pages/login/index.tsx | 147 +++++++++++++++-------------- src/pages/park/content-tab.tsx | 22 ++++- 3 files changed, 150 insertions(+), 79 deletions(-) diff --git a/src/pages/article-editor/index.tsx b/src/pages/article-editor/index.tsx index 06f6719..3d6f9bd 100644 --- a/src/pages/article-editor/index.tsx +++ b/src/pages/article-editor/index.tsx @@ -17,11 +17,12 @@ const CATS = [ { value: "dynamic", label: "动态" }, ]; -function stripHtml(html: string): string { - const div = document.createElement("div"); - div.innerHTML = html || ""; - return (div.textContent || "").replace(/\s+/g, " ").trim(); -} +/* 发布者身份徽章(存 source;C 端按此显示 官方/园区/OPC 徽章) */ +const PUB_TYPES = [ + { value: "official", label: "官方" }, + { value: "carrier", label: "园区" }, + { value: "opc", label: "OPC" }, +]; const uploadImage = async (file: File, insertFn: (url: string, alt: string, href: string) => void) => { try { const url = await contentApi.upload(file); insertFn(url, "", ""); } @@ -41,6 +42,9 @@ export default function ArticleEditor() { const [cat, setCat] = React.useState("policy"); const [title, setTitle] = React.useState(""); const [author, setAuthor] = React.useState(""); + const [publishAt, setPublishAt] = React.useState(""); // 发布时间(可未来=定时发布) + const [pubType, setPubType] = React.useState("official"); // 发布者身份(官方/园区/OPC) + const [summary, setSummary] = React.useState(""); // 导读(独立编辑) const [cover, setCover] = React.useState(""); // 加宽封面 3.35:1 const [coverSmall, setCoverSmall] = React.useState(""); // 小封面 1:1(可选) const [video, setVideo] = React.useState(""); @@ -60,12 +64,31 @@ export default function ArticleEditor() { const toolbarConfig: Partial = { excludeKeys: ["group-video"] }; const wordCount = editor ? editor.getText().replace(/\s/g, "").length : 0; + // ISO(UTC) → datetime-local 值;空返回空 + function toLocalInput(iso: string): string { + if (!iso) return ""; + const d = new Date(iso); + if (isNaN(d.getTime())) return ""; + const p = (n: number) => String(n).padStart(2, "0"); + return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`; + } + // datetime-local → ISO(UTC);空返回空 + function localToIso(v: string): string { + if (!v) return ""; + const d = new Date(v); + return isNaN(d.getTime()) ? "" : d.toISOString(); + } + React.useEffect(() => { if (!id) return; contentApi.getOne(id).then((r) => { setCat(r.type || "policy"); setTitle(r.title || ""); setAuthor(r.publisher_name || ""); setCover(r.cover || ""); setCoverSmall(r.cover_small || ""); setVideo(r.video || ""); setLink(r.link || ""); setHtml(r.body || ""); setCardMode(r.card_mode || "small"); + setPublishAt(toLocalInput(r.published_at || "")); + // 身份回填:识别 official/carrier/opc;旧数据 operator/其它 → 官方(carrier 保留园区) + setPubType(["official", "carrier", "opc"].includes(r.source || "") ? (r.source as string) : r.source === "carrier" ? "carrier" : "official"); + setSummary(r.summary || ""); }).catch((e) => setErr((e as Error).message || "加载失败")); return () => { if (editor) editor.destroy(); }; }, [id]); @@ -86,9 +109,11 @@ export default function ArticleEditor() { type: cat, title, body: html, cover, cover_small: coverSmall, video, link, publisher_name: author, + published_at: localToIso(publishAt), + source: pubType, card_mode: cardMode, status, - summary: stripHtml(html).slice(0, 80), + summary, }; const r = savedId ? await contentApi.update(savedId, body) : await contentApi.create(body); if (!savedId) setSavedId((r as { id: string }).id); @@ -153,7 +178,28 @@ export default function ArticleEditor() { onInput={(e) => { const t = e.currentTarget; t.style.height = "auto"; t.style.height = t.scrollHeight + "px"; }} />
{title.length}/64
- setAuthor(e.target.value)} /> + setAuthor(e.target.value)} /> +
+ + +
+
+