From 99a992f34b149d20a1b57a7aa89e26d572193cdb Mon Sep 17 00:00:00 2001 From: liangxu Date: Wed, 29 Apr 2026 19:57:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui/publish-records):=20surface=20=E5=B7=B2?= =?UTF-8?q?=E5=AD=98=E8=8D=89=E7=A8=BF=20status=20and=20client-only=20mana?= =?UTF-8?q?ge=20hint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render publish_type=draft tasks with the warn tone and 已存草稿 label, swap the management link copy to 打开草稿箱 for weixin_gzh, and expand the task summary to mention the publish_authorization_required reason. In the admin drawer, stop falling back to external_manage_url for missing public links and instead show a hint pointing operators back to the desktop client for weixin_gzh / zol records. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/components/ArticleDetailDrawer.vue | 16 +++++- apps/admin-web/src/i18n/messages/zh-CN.ts | 1 + .../renderer/views/PublishManagementView.vue | 49 +++++++++++++++++-- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/apps/admin-web/src/components/ArticleDetailDrawer.vue b/apps/admin-web/src/components/ArticleDetailDrawer.vue index 89b75f7..22b1142 100644 --- a/apps/admin-web/src/components/ArticleDetailDrawer.vue +++ b/apps/admin-web/src/components/ArticleDetailDrawer.vue @@ -229,7 +229,13 @@ async function refreshArticleData(): Promise { } function resolvePublishLink(record: PublishRecord): string { - return record.external_article_url || record.external_manage_url || ""; + return record.external_article_url?.trim() || ""; +} + +function shouldShowClientManageHint(record: PublishRecord): boolean { + return !resolvePublishLink(record) + && Boolean(record.external_manage_url?.trim()) + && ["weixin_gzh", "zol"].includes(record.platform_id); } async function copyPublishLink(record: PublishRecord): Promise { @@ -395,6 +401,9 @@ async function copyPublishLink(record: PublishRecord): Promise { + + {{ t("media.records.clientManageHint") }} + -- @@ -521,6 +530,11 @@ async function copyPublishLink(record: PublishRecord): Promise { font-size: 12px; } +.article-drawer__record-hint { + color: var(--muted); + font-size: 12px; +} + .article-drawer__record-link { flex: 1; min-width: 0; diff --git a/apps/admin-web/src/i18n/messages/zh-CN.ts b/apps/admin-web/src/i18n/messages/zh-CN.ts index 6d62fa4..804681a 100644 --- a/apps/admin-web/src/i18n/messages/zh-CN.ts +++ b/apps/admin-web/src/i18n/messages/zh-CN.ts @@ -1086,6 +1086,7 @@ const zhCN = { copy: "复制", empty: "暂无发布记录", linkEmpty: "当前记录还没有可用链接", + clientManageHint: "仅有管理页面,请在客户端打开管理页面查看", copySuccess: "链接已复制", }, }, diff --git a/apps/desktop-client/src/renderer/views/PublishManagementView.vue b/apps/desktop-client/src/renderer/views/PublishManagementView.vue index e322216..921c922 100644 --- a/apps/desktop-client/src/renderer/views/PublishManagementView.vue +++ b/apps/desktop-client/src/renderer/views/PublishManagementView.vue @@ -41,6 +41,8 @@ interface PublishTaskItem { updatedAt: number; externalArticleUrl: string | null; externalManageUrl: string | null; + publishType: string | null; + fallbackReason: string | null; errorMessage: string | null; } @@ -79,6 +81,13 @@ function statusLabel(status: DesktopTaskInfo["status"]): string { return map[status]; } +function statusLabelForTask(task: PublishTaskItem): string { + if (task.status === "succeeded" && task.publishType === "draft") { + return "已存草稿"; + } + return statusLabel(task.status); +} + function isPendingStatus(status: DesktopTaskInfo["status"]): boolean { return status === "queued" || status === "in_progress"; } @@ -98,6 +107,13 @@ function statusTone(status: DesktopTaskInfo["status"]): "info" | "warn" | "succe } } +function statusToneForTask(task: PublishTaskItem): "info" | "warn" | "success" | "danger" { + if (task.status === "succeeded" && task.publishType === "draft") { + return "warn"; + } + return statusTone(task.status); +} + function extractNumber(value: JsonValue | null | undefined): number | null { if (typeof value === "number" && Number.isFinite(value)) { return value; @@ -164,13 +180,23 @@ function inlineErrorPreview(message: string): string { return `${compact.slice(0, INLINE_ERROR_HEAD_CHARS)}...${compact.slice(-INLINE_ERROR_TAIL_CHARS)}`; } -function summaryForTask(status: DesktopTaskInfo["status"]): string { +function summaryForTask( + status: DesktopTaskInfo["status"], + publishType: string | null = null, + fallbackReason: string | null = null, +): string { switch (status) { case "queued": return "任务已进入队列,等前面的 PublishTasks 翻完后再继续展示历史记录。"; case "in_progress": return "当前正在由桌面端执行发送,请避免重复触发。"; case "succeeded": + if (publishType === "draft") { + if (fallbackReason === "publish_authorization_required") { + return "草稿已保存,需在公众号后台完成微信验证/授权发布。"; + } + return "文章已保存到草稿箱,需在平台后台完成发表。"; + } return "文章已成功发送到目标平台。"; case "failed": return "本次发送失败,可以手动再次发送。"; @@ -182,6 +208,13 @@ function summaryForTask(status: DesktopTaskInfo["status"]): string { } } +function manageLinkLabel(task: PublishTaskItem): string { + if (task.publishType === "draft" && task.platform === "weixin_gzh") { + return "打开草稿箱"; + } + return "打开管理页"; +} + function parseTimestamp(value: string | null): number { if (!value) { return Date.now(); @@ -376,6 +409,8 @@ const publishTasks = computed(() => const payload = task.payload ?? {}; const result = task.result ?? {}; const taskError = task.error ?? {}; + const publishType = extractString(result.publish_type); + const fallbackReason = extractString(result.fallback_reason); const nestedContentRef = payload.content_ref && typeof payload.content_ref === "object" && !Array.isArray(payload.content_ref) ? (payload.content_ref as Record) @@ -390,13 +425,17 @@ const publishTasks = computed(() => accountId: task.target_account_id, accountName: accountNameMap.value.get(task.target_account_id) ?? "待同步账号", status: normalizedStatus, - summary: summaryForTask(normalizedStatus), + summary: summaryForTask(normalizedStatus, publishType, fallbackReason), attempts: task.attempts, leaseExpiresAt: task.lease_expires_at ? parseTimestamp(task.lease_expires_at) : null, createdAt: parseTimestamp(task.created_at), updatedAt: parseTimestamp(task.updated_at), - externalArticleUrl: extractString(result.external_article_url) ?? buildSohuArticleUrl(task, result), + externalArticleUrl: publishType === "draft" + ? null + : extractString(result.external_article_url) ?? buildSohuArticleUrl(task, result), externalManageUrl: extractString(result.external_manage_url), + publishType, + fallbackReason, errorMessage: normalizeTaskErrorMessage( extractString(taskError.message) ?? extractString(taskError.detail) @@ -533,7 +572,7 @@ const tableScroll = { x: 1000 } as const;