From cde70ca5e0999fa2a0ef0f51b5b81a1847a8492b Mon Sep 17 00:00:00 2001 From: liangxu Date: Sat, 25 Apr 2026 23:16:45 +0800 Subject: [PATCH] feat(admin-web): trim platform and cover controls from article editor Remove the right-rail publish platform selector and cover picker from the in-editor view; those settings now live in the publish modal so the editor focuses on title and content. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../admin-web/src/views/ArticleEditorView.vue | 282 +----------------- 1 file changed, 4 insertions(+), 278 deletions(-) diff --git a/apps/admin-web/src/views/ArticleEditorView.vue b/apps/admin-web/src/views/ArticleEditorView.vue index e4bfd6b..784729b 100644 --- a/apps/admin-web/src/views/ArticleEditorView.vue +++ b/apps/admin-web/src/views/ArticleEditorView.vue @@ -7,19 +7,9 @@ import { useI18n } from "vue-i18n"; import { useRoute, useRouter } from "vue-router"; import ArticleEditorCanvas from "@/components/ArticleEditorCanvas.vue"; -import CoverPickerModal from "@/components/CoverPickerModal.vue"; import PublishArticleModal from "@/components/PublishArticleModal.vue"; -import PublishPlatformSelector from "@/components/PublishPlatformSelector.vue"; -import { articlesApi, mediaApi, resolveApiURL, tenantAccountsApi } from "@/lib/api"; -import { - coverUploadRequired, - deriveCoverFileName, -} from "@/lib/cover-requirements"; +import { articlesApi } from "@/lib/api"; import { formatError } from "@/lib/errors"; -import { - buildPublishPlatformOptions, - normalizePublishPlatformIds, -} from "@/lib/publish-platforms"; const route = useRoute(); const router = useRouter(); @@ -31,15 +21,6 @@ const title = ref(""); const markdown = ref(""); const initialTitle = ref(""); const initialMarkdown = ref(""); -const publishPlatforms = ref([]); -const initialPublishPlatforms = ref([]); -const coverEnabled = ref(false); -const coverAssetUrl = ref(""); -const coverImageAssetId = ref(null); -const coverFileName = ref(""); -const initialCoverAssetUrl = ref(""); -const initialCoverImageAssetId = ref(null); -const coverPickerOpen = ref(false); const leaveModalOpen = ref(false); const publishModalOpen = ref(false); @@ -49,24 +30,6 @@ const detailQuery = useQuery({ queryFn: () => articlesApi.detail(articleId.value), }); -const platformsQuery = useQuery({ - queryKey: ["media", "platforms", "article-editor"], - queryFn: () => mediaApi.platforms(), -}); - -const accountsQuery = useQuery({ - queryKey: ["tenant", "desktop-accounts", "article-editor"], - queryFn: () => tenantAccountsApi.list(), -}); - -const publishPlatformOptions = computed(() => - buildPublishPlatformOptions(platformsQuery.data.value ?? [], accountsQuery.data.value), -); - -const publishPlatformsLoading = computed( - () => platformsQuery.isPending.value || accountsQuery.isPending.value, -); - watch( () => detailQuery.data.value, (detail) => { @@ -81,45 +44,17 @@ watch( markdown.value = nextMarkdown; initialTitle.value = nextTitle; initialMarkdown.value = nextMarkdown; - publishPlatforms.value = normalizePublishPlatformIds(detail.platforms ?? []); - initialPublishPlatforms.value = [...publishPlatforms.value]; - coverAssetUrl.value = resolveApiURL(detail.cover_asset_url); - coverImageAssetId.value = detail.cover_image_asset_id ?? null; - coverFileName.value = deriveCoverFileName(coverAssetUrl.value); - initialCoverAssetUrl.value = coverAssetUrl.value; - initialCoverImageAssetId.value = coverImageAssetId.value; - coverEnabled.value = Boolean(coverAssetUrl.value); }, { immediate: true }, ); const detail = computed(() => detailQuery.data.value); const editorLocked = computed(() => detail.value?.generate_status !== "completed"); -const coverRequired = computed(() => coverUploadRequired(publishPlatforms.value)); -const normalizedCoverValue = computed(() => { - if (!coverEnabled.value && !coverRequired.value) { - return ""; - } - return coverAssetUrl.value.trim(); -}); const referencedImageAssetIds = computed(() => extractReferencedImageAssetIds(markdown.value)); const hasChanges = computed( () => title.value.trim() !== initialTitle.value.trim() || - markdown.value !== initialMarkdown.value || - serializePlatformSelection(publishPlatforms.value) !== serializePlatformSelection(initialPublishPlatforms.value) || - normalizedCoverValue.value !== initialCoverAssetUrl.value.trim() || - coverImageAssetId.value !== initialCoverImageAssetId.value, -); - -watch( - coverRequired, - (required) => { - if (required) { - coverEnabled.value = true; - } - }, - { immediate: true }, + markdown.value !== initialMarkdown.value, ); const saveMutation = useMutation({ @@ -127,10 +62,7 @@ const saveMutation = useMutation({ articlesApi.update(articleId.value, { title: title.value.trim(), markdown_content: markdown.value, - platforms: normalizePublishPlatformIds(publishPlatforms.value), - cover_asset_url: normalizedCoverValue.value, referenced_image_asset_ids: referencedImageAssetIds.value, - cover_image_asset_id: coverImageAssetId.value, }), onSuccess: async (data) => { message.success(t("article.editor.messages.saved")); @@ -138,14 +70,6 @@ const saveMutation = useMutation({ markdown.value = data.markdown_content ?? ""; initialTitle.value = title.value; initialMarkdown.value = markdown.value; - publishPlatforms.value = normalizePublishPlatformIds(data.platforms ?? []); - initialPublishPlatforms.value = [...publishPlatforms.value]; - coverAssetUrl.value = resolveApiURL(data.cover_asset_url); - coverImageAssetId.value = data.cover_image_asset_id ?? null; - coverFileName.value = deriveCoverFileName(coverAssetUrl.value); - initialCoverAssetUrl.value = coverAssetUrl.value; - initialCoverImageAssetId.value = coverImageAssetId.value; - coverEnabled.value = coverRequired.value || Boolean(coverAssetUrl.value); await Promise.all([ queryClient.invalidateQueries({ queryKey: ["articles"] }), queryClient.invalidateQueries({ queryKey: ["workspace"] }), @@ -222,19 +146,6 @@ async function uploadEditorImage(file: File): Promise { return result.url; } -function handleCoverPicked(payload: { url: string; fileName: string; assetId?: number | null }): void { - coverAssetUrl.value = payload.url; - coverImageAssetId.value = payload.assetId ?? null; - coverFileName.value = payload.fileName; - coverEnabled.value = true; -} - -function handleRemoveCover(): void { - coverAssetUrl.value = ""; - coverImageAssetId.value = null; - coverFileName.value = ""; -} - async function handlePublished(): Promise { await Promise.all([ detailQuery.refetch(), @@ -353,12 +264,6 @@ function extractReferencedImageAssetIds(markdownValue: string): number[] { return [...ids]; } -function serializePlatformSelection(platformIds: string[]): string { - return normalizePublishPlatformIds(platformIds) - .sort() - .join(","); -} - onMounted(() => { window.addEventListener("keydown", handleSaveShortcut); }); @@ -415,63 +320,6 @@ onBeforeUnmount(() => { :upload-image="uploadEditorImage" /> - - @@ -511,16 +359,6 @@ onBeforeUnmount(() => { :article-id="detail?.id ?? null" @published="handlePublished" /> - - @@ -577,8 +415,7 @@ onBeforeUnmount(() => { backdrop-filter: blur(14px); } -.article-editor-view__loading, -.article-editor-view__card { +.article-editor-view__loading { padding: 24px; background: rgba(255, 255, 255, 0.88); border: 1px solid rgba(216, 225, 237, 0.9); @@ -598,10 +435,7 @@ onBeforeUnmount(() => { } .article-editor-view__layout { - display: grid; - grid-template-columns: minmax(0, 1fr) 360px; - gap: 18px; - align-items: start; + min-width: 0; } .article-editor-view__main { @@ -610,114 +444,6 @@ onBeforeUnmount(() => { gap: 18px; } -.article-editor-view__card h3 { - margin: 0; - color: #101828; - font-size: 15px; - font-weight: 700; -} - -.article-editor-view__rail { - display: flex; - flex-direction: column; - gap: 18px; -} - -.article-editor-view__card p { - margin: 6px 0 0; - color: #667085; - font-size: 13px; - line-height: 1.7; -} - -.article-editor-view__platforms { - margin-top: 18px; -} - -.article-editor-view__card-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 12px; -} - -.article-editor-view__cover-panel { - display: grid; - grid-template-columns: 156px minmax(0, 1fr); - gap: 16px; - margin-top: 18px; -} - -.article-editor-view__cover-dropzone { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 8px; - min-height: 116px; - padding: 0; - border: 1px dashed #d3dceb; - border-radius: 20px; - background: - radial-gradient(circle at top, rgba(70, 102, 255, 0.08), transparent 45%), - #fbfcff; - color: #475467; - cursor: pointer; - overflow: hidden; -} - -.article-editor-view__cover-dropzone img { - width: 100%; - height: 116px; - object-fit: cover; -} - -.article-editor-view__cover-info { - display: flex; - flex-direction: column; - gap: 12px; -} - -.article-editor-view__cover-actions { - display: flex; - flex-wrap: wrap; - gap: 10px; -} - -.article-editor-view__cover-file, -.article-editor-view__cover-off { - color: #667085; - font-size: 13px; - line-height: 1.7; -} - -.article-editor-view__cover-plus { - display: inline-flex; - align-items: center; - justify-content: center; - width: 44px; - height: 44px; - border-radius: 999px; - background: #eef4ff; - color: #355dff; - font-size: 28px; - line-height: 1; -} - -@media (max-width: 1200px) { - .article-editor-view__layout { - grid-template-columns: 1fr; - } - - .article-editor-view__rail { - order: -1; - } - - .article-editor-view__cover-panel { - grid-template-columns: 1fr; - } -} - @media (max-width: 768px) { .article-editor-view__topbar { flex-direction: column;