feat: add cover image functionality for articles

- Implemented `resolveApiURL` function to handle various URL formats.
- Updated `ArticleDetail` and `UpdateArticleRequest` interfaces to include `cover_asset_url`.
- Enhanced `ArticleEditorView` to manage cover image uploads, including a new `CoverPickerModal` component.
- Added cover image requirements logic in `cover-requirements.ts` to enforce platform-specific cover image rules.
- Modified backend services to handle cover image uploads and validations, including checks for required cover images for specific platforms.
- Improved error handling for cover image requirements during article publishing.
This commit is contained in:
2026-04-07 09:19:03 +08:00
parent 1e844704e4
commit 9f721f6088
13 changed files with 1360 additions and 129 deletions
+51 -2
View File
@@ -427,8 +427,15 @@ const enUS = {
platformsTitle: "Publish platforms",
platformsHint: "This stays in sync with Media Management so you can edit the article's publish platforms directly.",
coverTitle: "Cover image",
coverHint: "Upload a cover asset before publishing. This is local preview only for now.",
coverHint: "Off by default. Once uploaded, the cover is saved with the article.",
coverRequired: "Baijiahao is selected, so the cover image becomes required.",
coverUpload: "Upload cover image",
coverReplace: "Choose again",
coverRemove: "Remove cover",
coverSaved: "Cover asset saved",
coverOff: "Cover image is currently off. Turn it on to upload and crop a cover asset.",
coverTagRequired: "Required",
coverTagOptional: "Optional",
imagePrompt: "Enter image URL",
tablePicker: {
empty: "Drag to choose a table size",
@@ -566,7 +573,8 @@ const enUS = {
platformsTitle: "Target platforms",
platformsHint: "Previously selected and bound accounts are prefilled first. Unbound or locally unavailable platforms stay disabled.",
coverTitle: "Cover image",
coverHint: "This version uses a cover asset URL. Upload and crop can be wired later.",
coverHint: "Off by default. Uploading opens the cover picker and cropper.",
coverUpload: "Upload cover image",
coverPlaceholder: "Enter an optional cover asset URL",
messages: {
success: "{count} publish tasks submitted.",
@@ -574,6 +582,7 @@ const enUS = {
partial: "Publish finished with {success} succeeded and {failed} failed.",
partialTitle: "Partial publish failure",
selectPlatform: "Choose at least one available platform first.",
coverRequired: "Baidu Baijiahao is selected. Please upload a cover image first.",
failureTitle: "Publish failed",
failureItem: "Publishing to [{platform}] failed: [{reason}]",
unknownFailure: "Unknown error",
@@ -592,6 +601,46 @@ const enUS = {
copySuccess: "Link copied.",
},
},
coverPicker: {
title: "Select cover image",
tabs: {
local: "Body / Local Upload",
ai: "AI Cover",
},
previewTitle: "Cover crop preview",
previewHintLocked: "Baijiahao is selected, so the cover will be exported as 16:9 when you confirm.",
previewHintFree: "There is no forced cover ratio right now. You can upload the original image directly or adjust framing before confirming.",
requirementsTitle: "Platform requirements",
requirementsHint: "Requirements switch automatically based on the selected publish platforms. Baijiahao is mandatory.",
recommendedRatio: "Recommended ratio",
recommendedSize: "Recommended size",
required: "Required",
optional: "Optional",
zoom: "Zoom",
fileLabel: "Current cover",
emptyTitle: "No publish platform selected",
emptyPlatforms: "Choose publish platforms first and each platform's cover requirements will appear here.",
emptyHint: "Supports PNG, JPG, GIF, and WebP up to 10MB.",
aiTitle: "AI cover is coming soon",
aiHint: "For now this flow supports local upload and cropping. AI cover generation can be plugged in here later.",
actions: {
upload: "Local upload",
reset: "Reset framing",
remove: "Remove cover",
},
notes: {
required: "This platform currently requires a cover image in the publish flow. Publishing is blocked until one is uploaded.",
optional: "This platform already supports standalone covers and reuses the current crop when publishing.",
unsupported: "This platform's current integration does not consume a standalone cover yet and falls back to body content or the platform default.",
},
messages: {
invalidType: "Only PNG, JPG, GIF, and WebP images are supported.",
tooLarge: "Image size cannot exceed 10MB.",
missingImage: "Select a cover image first.",
missingArticle: "The article is not ready yet. Refresh and try again.",
imageLoadFailed: "Failed to load the cover image. Please choose another image.",
},
},
brands: {
eyebrow: "Brand Library",
title: "Brand Library",