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
@@ -78,6 +78,31 @@ func extractPlatformsFromJSONPayload(raw []byte) []string {
return nil
}
func resolveArticleCoverAssetURL(wizardStateJSON []byte) *string {
value := strings.TrimSpace(extractStringFromJSONPayload(wizardStateJSON, "cover_asset_url"))
if value == "" {
return nil
}
return &value
}
func extractStringFromJSONPayload(raw []byte, key string) string {
if len(raw) == 0 {
return ""
}
var payload map[string]interface{}
if err := json.Unmarshal(raw, &payload); err != nil {
return ""
}
value, ok := payload[key].(string)
if !ok {
return ""
}
return strings.TrimSpace(value)
}
func normalizePlatformsFromValue(raw interface{}) []string {
switch typed := raw.(type) {
case nil:
@@ -101,7 +126,7 @@ func normalizePlatformsFromValue(raw interface{}) []string {
}
}
func mergeArticleWizardState(raw []byte, title string, platforms []string) ([]byte, error) {
func mergeArticleWizardState(raw []byte, title string, platforms []string, coverAssetURL *string) ([]byte, error) {
state := make(map[string]interface{})
if len(raw) > 0 {
_ = json.Unmarshal(raw, &state)
@@ -123,5 +148,14 @@ func mergeArticleWizardState(raw []byte, title string, platforms []string) ([]by
}
}
if coverAssetURL != nil {
trimmedCoverURL := strings.TrimSpace(*coverAssetURL)
if trimmedCoverURL == "" {
delete(state, "cover_asset_url")
} else {
state["cover_asset_url"] = trimmedCoverURL
}
}
return json.Marshal(state)
}