feat: add metadata console and seo ai rule pages

This commit is contained in:
root
2026-04-18 20:23:30 +08:00
parent 845d68110b
commit 2d1834422b
14 changed files with 1769 additions and 50 deletions

View File

@@ -1,5 +1,60 @@
<template>
<div class="home card">
<el-card shadow="never" class="home-metadata-console">
<template #header>
<div class="home-metadata-console__header">
<div>
<div class="home-metadata-console__title">视频缺字段控制台</div>
<div class="home-metadata-console__subtitle">后台打开先看这里工作台任务池和计划任务状态都汇总到首屏</div>
</div>
<div class="home-metadata-console__actions">
<el-button :loading="metadataLoading" @click="loadMetadataConsole">刷新状态</el-button>
<el-button type="primary" :loading="workbenchRunning" @click="handleRunWorkbench">刷新工作台</el-button>
<el-button type="warning" plain :loading="taskPoolRunning" @click="handleRunTaskPool">刷新任务池</el-button>
</div>
</div>
</template>
<el-alert
:type="metadataAlertType"
:closable="false"
show-icon
:title="metadataAlertTitle"
:description="metadataAlertDescription"
/>
<div class="home-metadata-console__stats">
<div class="home-metadata-stat">
<div class="home-metadata-stat__label">缺字段视频总量</div>
<div class="home-metadata-stat__value">{{ metadataSummary.workbench?.videos_with_any_missing_metadata || 0 }}</div>
<div class="home-metadata-stat__tip">越大越需要先看工作台再按批次派给 Codex</div>
</div>
<div class="home-metadata-stat">
<div class="home-metadata-stat__label">重采优先队列</div>
<div class="home-metadata-stat__value">{{ metadataSummary.workbench?.queue_size || 0 }}</div>
<div class="home-metadata-stat__tip">这是最适合优先处理的一批适合先做首轮修复</div>
</div>
<div class="home-metadata-stat">
<div class="home-metadata-stat__label">任务池批次数</div>
<div class="home-metadata-stat__value">{{ metadataSummary.task_pool?.batch_count || 0 }}</div>
<div class="home-metadata-stat__tip">批次越清晰后续技术和 Codex 的协作越顺</div>
</div>
</div>
<div class="home-metadata-console__hint">
<strong>当前建议</strong>{{ metadataSummary.recommended_action || "先刷新状态,再看工作台和任务池。" }}
<br />
<strong>首条提示</strong>{{ metadataFirstHint }}
</div>
<div class="home-metadata-console__links">
<el-button text type="primary" @click="openMetadataRoute('/video/metadata/missing/task-pool/status')">打开状态总览</el-button>
<el-button text type="primary" @click="openMetadataRoute('/video/metadata/missing/task-pool/ops')">打开运维总览</el-button>
<el-button text type="primary" @click="openMetadataRoute('/video/metadata/missing/task-pool')">打开任务池</el-button>
<el-button text type="primary" @click="openMetadataRoute('/video/metadata/missing/workbench')">打开工作台</el-button>
</div>
</el-card>
<div class="home-bootstrap-actions">
<el-button type="primary" @click="openBootstrapCenter('overview')">打开 Bootstrap 总控台</el-button>
<el-button type="warning" plain @click="openBootstrapCenter({ tab: 'ops', opsFocus: { panel: 'oncall', stage: 'all', clearHostFocus: true, hostConsolePanel: 'oncall' } })">值班待办</el-button>
@@ -13,14 +68,25 @@
</template>
<script setup lang="ts" name="home">
import { computed, onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import BootstrapOpsOverview from "@/views/site/bootstrapOpsOverview.vue";
import BootstrapEnvOverview from "@/views/site/bootstrapEnvOverview.vue";
import BootstrapAdminCenter from "@/views/site/bootstrapAdminCenter.vue";
import { ref } from "vue";
import {
getMetadataMissingTaskPoolStatus,
runMetadataMissingTaskPool,
runMetadataMissingWorkbench
} from "@/api/modules/video/list";
import type { VideoList } from "@/api/interface/video/list";
const router = useRouter();
const bootstrapAdminCenterRef = ref<InstanceType<typeof BootstrapAdminCenter> | null>(null);
const metadataLoading = ref(false);
const workbenchRunning = ref(false);
const taskPoolRunning = ref(false);
const metadataSummary = ref<VideoList.MetadataMissingTaskPoolStatusData>({});
type BootstrapFocusMode = "all" | "task" | "oncall" | "pipeline" | "import" | "release";
type BootstrapFocusPayload = BootstrapFocusMode | BootstrapFocusMode[];
type BootstrapComparePreset =
@@ -77,6 +143,69 @@ const openBootstrapCenter = (payload: BootstrapAdminOpenPayload = "overview") =>
bootstrapAdminCenterRef.value?.openDialog(payload);
};
const loadMetadataConsole = async () => {
metadataLoading.value = true;
try {
const response = await getMetadataMissingTaskPoolStatus();
metadataSummary.value = response.data || {};
} finally {
metadataLoading.value = false;
}
};
const handleRunWorkbench = async () => {
workbenchRunning.value = true;
try {
const response = await runMetadataMissingWorkbench();
ElMessage.success(response.msg || "工作台刷新已触发");
await loadMetadataConsole();
} finally {
workbenchRunning.value = false;
}
};
const handleRunTaskPool = async () => {
taskPoolRunning.value = true;
try {
const response = await runMetadataMissingTaskPool();
ElMessage.success(response.msg || "任务池刷新已触发");
await loadMetadataConsole();
} finally {
taskPoolRunning.value = false;
}
};
const openMetadataRoute = (path: string) => {
router.push(path);
};
const metadataFirstHint = computed(() => metadataSummary.value.operator_hint?.[0] || "当前还没有额外提示,建议先看状态总览。");
const metadataAlertType = computed(() => {
const status = metadataSummary.value.plan_task?.status_label || "missing";
if (status === "enabled") return "success";
if (status === "disabled") return "warning";
if (status === "db_blocked") return "error";
return "info";
});
const metadataAlertTitle = computed(() => {
const status = metadataSummary.value.plan_task?.status_label || "missing";
if (status === "enabled") return "视频缺字段计划任务已启用";
if (status === "disabled") return "视频缺字段计划任务已存在但未启用";
if (status === "db_blocked") return "当前环境暂时无法读取视频缺字段计划任务";
return "视频缺字段计划任务尚未补齐";
});
const metadataAlertDescription = computed(() => {
const status = metadataSummary.value.plan_task?.status_label || "missing";
const action = metadataSummary.value.recommended_action || "先刷新状态,再决定下一步。";
if (status === "enabled") return `当前工作台和任务池已经可持续刷新。${action}`;
if (status === "disabled") return `当前后台已经有这条计划任务,但还没有启用。${action}`;
if (status === "db_blocked") return `这通常说明当前后台环境没读到完整数据库上下文。${action}`;
return `当前还没补齐这条计划任务,建议先补齐再观察。${action}`;
});
const goSitePage = (payload: BootstrapDialogPayload = "all") => {
const normalizedFocus = typeof payload === "string" || Array.isArray(payload) ? payload : payload.focusMode ?? "all";
const comparePreset = typeof payload === "string" || Array.isArray(payload) ? undefined : payload.comparePreset;
@@ -93,6 +222,10 @@ const goSitePage = (payload: BootstrapDialogPayload = "all") => {
}
});
};
onMounted(() => {
loadMetadataConsole();
});
</script>
<style scoped lang="scss">