From 2d1834422bb336be8d01c72a3dababa0e68dbf55 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 18 Apr 2026 20:23:30 +0800 Subject: [PATCH] feat: add metadata console and seo ai rule pages --- src/api/config/index.ts | 1 + src/api/interface/site/list.ts | 71 +++- src/api/interface/system/config.ts | 6 + src/api/interface/video/list.ts | 121 ++++++ src/api/modules/system/config.ts | 6 +- src/api/modules/video/list.ts | 24 ++ src/assets/json/authMenuList.json | 129 ++++-- src/views/home/index.scss | 98 +++++ src/views/home/index.vue | 135 +++++- src/views/site/index.vue | 335 ++++++++++++++- src/views/system/config/drawer.vue | 24 +- src/views/system/config/index.vue | 119 +++++- src/views/system/config/seoAiRules.vue | 254 +++++++++++ src/views/video/metadataMissing/index.vue | 496 ++++++++++++++++++++++ 14 files changed, 1769 insertions(+), 50 deletions(-) create mode 100644 src/views/system/config/seoAiRules.vue create mode 100644 src/views/video/metadataMissing/index.vue diff --git a/src/api/config/index.ts b/src/api/config/index.ts index 98743e0..08baf61 100644 --- a/src/api/config/index.ts +++ b/src/api/config/index.ts @@ -2,6 +2,7 @@ export const loginApiUrl = "/guest/login"; export const getSystemConfigListApiUrl = "/system/config/list"; export const getSystemConfigSaveApiUrl = "/system/config/save"; +export const getSystemConfigSeoAiRulesResetApiUrl = "/system/config/seo-ai-rules/reset"; export const getSystemPlanListApiUrl = "/system/plan/list"; export const getSystemPlanSaveApiUrl = "/system/plan/save"; diff --git a/src/api/interface/site/list.ts b/src/api/interface/site/list.ts index 71823cd..cb7003e 100644 --- a/src/api/interface/site/list.ts +++ b/src/api/interface/site/list.ts @@ -924,7 +924,15 @@ export namespace SiteList { skipped_template_missing_count?: number; skipped_duplicate_in_file_count?: number; strategy_profile_counts?: Record; - inserted_domains?: Array<{ d_id?: number; d_domain?: string; strategy_profile?: SeoStrategyProfile }>; + inserted_domains?: Array<{ + d_id?: number; + d_domain?: string; + d_name?: string; + t_id?: number; + strategy_profile?: SeoStrategyProfile; + tkd_provider?: string; + tkd_mode?: string; + }>; skipped_domains?: Array<{ d_domain?: string; reason?: string; t_id?: number }>; run_root?: string; uploaded_excel_path?: string; @@ -934,6 +942,67 @@ export namespace SiteList { probe_failed_count?: number; summary_json_path?: string; summary_html_path?: string; + next_step_notice?: string[]; + codex_continue_prompt?: string; + post_import_workflow?: { + enabled?: boolean; + auto_ai_after_import?: boolean; + summary?: string; + inserted_domain_count?: number; + inserted_domain_hosts?: string[]; + contains_openai_domains?: boolean; + artifact_audit?: { + summary?: { + total?: number; + state_ready_count?: number; + manifest_ready_count?: number; + positioning_ready_count?: number; + published_copy_ready_count?: number; + sitemap_generated_count?: number; + ai_pending_count?: number; + }; + items?: Array<{ + host?: string; + site_name?: string; + state_exists?: boolean; + manifest_exists?: boolean; + site_positioning_exists?: boolean; + published_copy_exists?: boolean; + ai_status?: string; + wait_for_ai_before_publish?: boolean; + sitemap_status?: "generated" | "queued_pending" | string; + existing_sitemaps?: string[]; + notes?: string[]; + }>; + }; + recommended_for_operator?: "openai_continue" | "codex_continue"; + recommended_for_technical?: "openai_continue" | "codex_continue"; + notices?: string[]; + choices?: Array<{ + key?: "openai_continue" | "codex_continue"; + label?: string; + audience?: string; + description?: string; + }>; + openai?: { + domain_ids?: number[]; + domain_id_csv?: string; + hosts?: string[]; + probe_status?: "passed" | "partial" | "failed" | "skipped" | string; + probe_summary_html_path?: string; + probe_summary_json_path?: string; + operator_steps?: string[]; + }; + codex?: { + workspace_path?: string; + open_in_vscode_hint?: string; + prompt?: string; + }; + ui_suggestion?: { + message?: string; + button_labels?: string[]; + }; + }; } export interface DomainImportRunSummaryItem { diff --git a/src/api/interface/system/config.ts b/src/api/interface/system/config.ts index 2309817..92b221b 100644 --- a/src/api/interface/system/config.ts +++ b/src/api/interface/system/config.ts @@ -9,6 +9,9 @@ export namespace ConfigList { sc_val: string; sc_description: string; } + export interface resetSeoAiRuleParams { + sc_code?: string; + } export interface delParams { sc_id: string; } @@ -18,5 +21,8 @@ export namespace ConfigList { sc_val: string; sc_description: string; } + export interface SeoAiRuleCard extends ResList { + note?: Partial | null; + } export interface ResData extends ResultData {} } diff --git a/src/api/interface/video/list.ts b/src/api/interface/video/list.ts index fbda01a..2e69411 100644 --- a/src/api/interface/video/list.ts +++ b/src/api/interface/video/list.ts @@ -78,4 +78,125 @@ export namespace VideoList { tag_list?: any; } export interface ResData extends ResultData {} + + export interface MetadataMissingTaskPoolPlanTaskStatus { + exists?: boolean; + enabled?: boolean; + limit_seconds?: number; + limit_human?: string; + status_label?: string; + db_ready?: boolean; + db_error?: string; + pt_id?: number; + pt_name?: string; + pt_code?: string; + last_run_at?: string; + } + + export interface MetadataMissingTaskPoolStatusData { + generated_at?: string; + recommended_action?: string; + operator_hint?: string[]; + workbench?: { + generated_at?: string; + videos_with_any_missing_metadata?: number; + queue_size?: number; + workbench_index_path?: string; + latest_run?: Record; + }; + task_pool?: { + generated_at?: string; + batch_count?: number; + pending_count?: number; + processing_count?: number; + done_count?: number; + skipped_count?: number; + task_pool_index_path?: string; + }; + plan_task?: MetadataMissingTaskPoolPlanTaskStatus; + } + + export interface MetadataMissingTaskPoolOpsData { + summary?: MetadataMissingTaskPoolStatusData; + ops?: { + generated_at?: string; + recommended_action?: string; + command_entries?: Array<{ + label?: string; + command?: string; + }>; + artifact_entries?: Array<{ + label?: string; + path?: string; + }>; + }; + } + + export interface MetadataMissingWorkbenchSummaryData { + generated_at?: string; + sample_limit?: number; + queue_limit?: number; + prompt_limit?: number; + run_id?: string; + top_missing_fields?: Array<{ + field?: string; + missing_count?: number; + missing_ratio?: number; + }>; + next_actions?: Array<{ + key?: string; + label?: string; + summary?: string; + }>; + operator_notes?: Array<{ + title?: string; + content?: string; + }>; + queue?: { + queue_size?: number; + items?: Array>; + }; + audit?: { + total_videos?: number; + videos_with_any_missing_metadata?: number; + field_stats?: Array>; + }; + codex_dispatch_prompt?: string; + } + + export interface MetadataMissingTaskPoolData { + generated_at?: string; + source_generated_at?: string; + batch_size?: number; + batch_limit?: number; + total_videos?: number; + videos_with_any_missing_metadata?: number; + queue_size?: number; + operator_hint?: string; + top_missing_fields?: Array<{ + field?: string; + missing_count?: number; + missing_ratio?: number; + }>; + todo_summary?: Array<{ + batch_id?: string; + label?: string; + focus_field?: string; + items_count?: number; + priority_level?: string; + }>; + batches?: Array<{ + batch_id?: string; + batch_no?: number; + batch_label?: string; + focus_field?: string; + priority_level?: string; + items_count?: number; + status_label?: string; + owner?: string; + history_count?: number; + codex_prompt?: string; + items?: Array>; + }>; + } } diff --git a/src/api/modules/system/config.ts b/src/api/modules/system/config.ts index 9b9d67f..bd813c1 100644 --- a/src/api/modules/system/config.ts +++ b/src/api/modules/system/config.ts @@ -1,6 +1,6 @@ import { ResPage, ResultData } from "@/api/interface/index"; import { ConfigList } from "@/api/interface/system/config"; -import { getSystemConfigListApiUrl, getSystemConfigSaveApiUrl } from "@/api/config"; +import { getSystemConfigListApiUrl, getSystemConfigSaveApiUrl, getSystemConfigSeoAiRulesResetApiUrl } from "@/api/config"; import http from "@/api"; @@ -12,3 +12,7 @@ export const getConfigList = (params: ConfigList.ReqParams) => { export const SaveApi = (params: ConfigList.saveParams) => { return http.post>(getSystemConfigSaveApiUrl, params); }; + +export const resetSeoAiRulesApi = (params: ConfigList.resetSeoAiRuleParams) => { + return http.post>(getSystemConfigSeoAiRulesResetApiUrl, params); +}; diff --git a/src/api/modules/video/list.ts b/src/api/modules/video/list.ts index c5b6808..f6c490e 100644 --- a/src/api/modules/video/list.ts +++ b/src/api/modules/video/list.ts @@ -19,3 +19,27 @@ export const batchData = (params: VideoList.delParams) => { export const batchVipData = (params: VideoList.delParams) => { return http.post>(getVideoSetLevelApiUrl, params); }; + +export const getMetadataMissingTaskPoolStatus = () => { + return http.get(`/video/metadata/missing/task-pool/status`); +}; + +export const getMetadataMissingTaskPoolOps = () => { + return http.get(`/video/metadata/missing/task-pool/ops`); +}; + +export const getMetadataMissingWorkbenchSummary = () => { + return http.get(`/video/metadata/missing/workbench`); +}; + +export const getMetadataMissingTaskPoolSummary = () => { + return http.get(`/video/metadata/missing/task-pool`); +}; + +export const runMetadataMissingWorkbench = () => { + return http.post(`/video/metadata/missing/workbench/run`, {}); +}; + +export const runMetadataMissingTaskPool = () => { + return http.post(`/video/metadata/missing/task-pool/run`, {}); +}; diff --git a/src/assets/json/authMenuList.json b/src/assets/json/authMenuList.json index e897a4e..79a8975 100644 --- a/src/assets/json/authMenuList.json +++ b/src/assets/json/authMenuList.json @@ -119,26 +119,41 @@ "isKeepAlive": true }, "children": [ - { - "path": "/system/config/index", - "name": "systemConfigIndex", - "component": "/system/config/index", - "meta": { + { + "path": "/system/config/index", + "name": "systemConfigIndex", + "component": "/system/config/index", + "meta": { "an_id": 1201, "icon": "Histogram", "title": "配置列表", "isLink": "", "isHide": false, "isFull": false, - "isAffix": false, - "isKeepAlive": true - } - }, - { - "path": "/resource/endpoint/list", - "name": "endpointList", - "component": "/resource/endpoint/index", - "meta": { + "isAffix": false, + "isKeepAlive": true + } + }, + { + "path": "/system/config/seo-ai-rules", + "name": "systemConfigSeoAiRules", + "component": "/system/config/seoAiRules", + "meta": { + "an_id": 1202, + "icon": "MagicStick", + "title": "SEO AI规则", + "isLink": "", + "isHide": false, + "isFull": false, + "isAffix": false, + "isKeepAlive": true + } + }, + { + "path": "/resource/endpoint/list", + "name": "endpointList", + "component": "/resource/endpoint/index", + "meta": { "an_id": 2504, "icon": "Operation", "title": "端点资源列表", @@ -443,25 +458,85 @@ } }, - { - "path": "/video/index", - "name": "Video", - "component": "/video/index", - "meta": { + { + "path": "/video/index", + "name": "Video", + "component": "/video/index", + "meta": { "an_id": 2101, "icon": "VideoCamera", "title": "视频列表", "isLink": "", "isHide": false, "isFull": false, - "isAffix": false, - "isKeepAlive": true - } - }, - - { - "path": "/searchGroup/index", - "name": "searchGroupIndex", + "isAffix": false, + "isKeepAlive": true + } + }, + { + "path": "/video/metadata/missing/workbench", + "name": "videoMetadataMissingWorkbench", + "component": "/video/metadataMissing/index", + "meta": { + "an_id": 2110, + "icon": "DataAnalysis", + "title": "缺字段工作台", + "isLink": "", + "isHide": false, + "isFull": false, + "isAffix": false, + "isKeepAlive": true + } + }, + { + "path": "/video/metadata/missing/task-pool", + "name": "videoMetadataMissingTaskPool", + "component": "/video/metadataMissing/index", + "meta": { + "an_id": 2111, + "icon": "Tickets", + "title": "缺字段任务池", + "isLink": "", + "isHide": false, + "isFull": false, + "isAffix": false, + "isKeepAlive": true + } + }, + { + "path": "/video/metadata/missing/task-pool/status", + "name": "videoMetadataMissingTaskPoolStatus", + "component": "/video/metadataMissing/index", + "meta": { + "an_id": 2113, + "icon": "DataLine", + "title": "缺字段状态总览", + "isLink": "", + "isHide": true, + "isFull": false, + "isAffix": false, + "isKeepAlive": true + } + }, + { + "path": "/video/metadata/missing/task-pool/ops", + "name": "videoMetadataMissingTaskPoolOps", + "component": "/video/metadataMissing/index", + "meta": { + "an_id": 2114, + "icon": "Tools", + "title": "缺字段运维总览", + "isLink": "", + "isHide": true, + "isFull": false, + "isAffix": false, + "isKeepAlive": true + } + }, + + { + "path": "/searchGroup/index", + "name": "searchGroupIndex", "component": "/searchGroup/index", "meta": { "an_id": 2109, diff --git a/src/views/home/index.scss b/src/views/home/index.scss index 53f321e..01bd0b3 100644 --- a/src/views/home/index.scss +++ b/src/views/home/index.scss @@ -6,3 +6,101 @@ width: 100%; gap: 20px; } + +.home-metadata-console { + border: 1px solid #e5e7eb; + border-radius: 20px; + background: linear-gradient(135deg, #fffdf4 0%, #ffffff 58%, #eef6ff 100%); + + &__header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + flex-wrap: wrap; + } + + &__title { + font-size: 22px; + font-weight: 800; + line-height: 1.2; + color: #111827; + } + + &__subtitle { + margin-top: 8px; + color: #6b7280; + line-height: 1.7; + } + + &__actions { + display: flex; + gap: 12px; + flex-wrap: wrap; + } + + &__stats { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 14px; + margin-top: 18px; + } + + &__hint { + margin-top: 16px; + padding: 14px 16px; + border-radius: 14px; + background: rgba(255, 255, 255, 0.82); + border: 1px solid #e5e7eb; + line-height: 1.8; + color: #374151; + } + + &__links { + display: flex; + flex-wrap: wrap; + gap: 8px 12px; + margin-top: 12px; + } +} + +.home-metadata-stat { + padding: 16px 18px; + border-radius: 16px; + background: rgba(255, 255, 255, 0.9); + border: 1px solid #e5e7eb; + box-shadow: 0 8px 20px rgba(15, 23, 42, 0.04); + + &__label { + font-size: 13px; + color: #6b7280; + } + + &__value { + margin-top: 8px; + font-size: 30px; + font-weight: 800; + line-height: 1.1; + color: #111827; + } + + &__tip { + margin-top: 10px; + color: #6b7280; + line-height: 1.7; + font-size: 13px; + } +} + +.home-bootstrap-actions { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin: 0 0 16px; +} + +@media (max-width: 960px) { + .home-metadata-console__stats { + grid-template-columns: 1fr; + } +} diff --git a/src/views/home/index.vue b/src/views/home/index.vue index 092e35b..77339bc 100644 --- a/src/views/home/index.vue +++ b/src/views/home/index.vue @@ -1,5 +1,60 @@ diff --git a/src/views/system/config/drawer.vue b/src/views/system/config/drawer.vue index b0e6c97..7d1360a 100644 --- a/src/views/system/config/drawer.vue +++ b/src/views/system/config/drawer.vue @@ -22,7 +22,13 @@