debug
This commit is contained in:
256
src/views/site/domainExternalSeoCnOverviewDialog.vue
Normal file
256
src/views/site/domainExternalSeoCnOverviewDialog.vue
Normal file
@@ -0,0 +1,256 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" title="大陆 SEO 总览" width="1220px" draggable>
|
||||
<div class="cn-overview-dialog">
|
||||
<div class="cn-overview-dialog__toolbar">
|
||||
<el-button type="primary" plain @click="loadSummary">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
:title="summary.health_label ? `当前大盘状态:${buildHealthLabel(summary.health_label)}` : '当前还没有大陆 SEO 总览数据'"
|
||||
:description="summary.note || '这里会把快照摘要、收录变化、关键词升降和自动建议聚合到一个视角。'"
|
||||
:type="buildHealthType(summary.health_label)"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
|
||||
<div class="cn-overview-dialog__hero">
|
||||
<div class="cn-overview-dialog__hero-main">
|
||||
<div class="cn-overview-dialog__hero-label">大陆 SEO 大盘</div>
|
||||
<div class="cn-overview-dialog__hero-value">{{ buildHealthLabel(summary.health_label) }}</div>
|
||||
<div class="cn-overview-dialog__hero-note">
|
||||
先看收录和关键词是否同时在变好,再结合优先建议判断下一步要补哪里。
|
||||
</div>
|
||||
</div>
|
||||
<div class="cn-overview-dialog__hero-metrics">
|
||||
<div v-for="item in buildHeroMetrics(summary)" :key="item.label" class="metric-card metric-card--hero">
|
||||
<div class="metric-card__label">{{ item.label }}</div>
|
||||
<div class="metric-card__value">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cn-overview-dialog__metrics">
|
||||
<div class="metric-card"><div class="metric-card__label">Host</div><div class="metric-card__value">{{ summary.snapshot_summary?.host_count || 0 }}</div></div>
|
||||
<div class="metric-card"><div class="metric-card__label">indexed_like</div><div class="metric-card__value">{{ summary.snapshot_summary?.indexed_like_host_count || 0 }}</div></div>
|
||||
<div class="metric-card"><div class="metric-card__label">keyword_ready</div><div class="metric-card__value">{{ summary.snapshot_summary?.keyword_ready_host_count || 0 }}</div></div>
|
||||
<div class="metric-card"><div class="metric-card__label">traffic_ready</div><div class="metric-card__value">{{ summary.snapshot_summary?.traffic_ready_host_count || 0 }}</div></div>
|
||||
<div class="metric-card"><div class="metric-card__label">newly_indexed</div><div class="metric-card__value">{{ summary.index_movement?.newly_indexed_count || 0 }}</div></div>
|
||||
<div class="metric-card"><div class="metric-card__label">lost_indexed</div><div class="metric-card__value">{{ summary.index_movement?.lost_indexed_count || 0 }}</div></div>
|
||||
</div>
|
||||
|
||||
<div class="cn-overview-dialog__metrics cn-overview-dialog__metrics--priority">
|
||||
<div class="metric-card metric-card--priority-high"><div class="metric-card__label">high</div><div class="metric-card__value">{{ summary.priority_buckets?.high || 0 }}</div></div>
|
||||
<div class="metric-card metric-card--priority-medium"><div class="metric-card__label">medium</div><div class="metric-card__value">{{ summary.priority_buckets?.medium || 0 }}</div></div>
|
||||
<div class="metric-card metric-card--priority-low"><div class="metric-card__label">low</div><div class="metric-card__value">{{ summary.priority_buckets?.low || 0 }}</div></div>
|
||||
<div class="metric-card"><div class="metric-card__label">keyword_up</div><div class="metric-card__value">{{ summary.keyword_movement?.up_count || 0 }}</div></div>
|
||||
<div class="metric-card"><div class="metric-card__label">keyword_down</div><div class="metric-card__value">{{ summary.keyword_movement?.down_count || 0 }}</div></div>
|
||||
<div class="metric-card"><div class="metric-card__label">keyword_dropped</div><div class="metric-card__value">{{ summary.keyword_movement?.dropped_count || 0 }}</div></div>
|
||||
</div>
|
||||
|
||||
<div class="cn-overview-dialog__descriptions">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="latest_snapshot_date">{{ summary.snapshot_summary?.latest_metric_date || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="latest_index_date">{{ summary.index_movement?.latest_metric_date || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="latest_keyword_date">{{ summary.keyword_movement?.latest_metric_date || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="health_label">{{ summary.health_label || "-" }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div class="cn-overview-dialog__attention">
|
||||
<div class="pane-card">
|
||||
<div class="pane-card__title">大盘重点 Host</div>
|
||||
<div v-if="!(summary.top_hosts || []).length" class="pane-card__empty">当前还没有重点 Host 数据</div>
|
||||
<div v-else class="cn-overview-dialog__focus-list">
|
||||
<div v-for="(item, index) in (summary.top_hosts || []).slice(0, 3)" :key="`${item.host || 'host'}-${index}`" class="cn-overview-dialog__focus-item">
|
||||
<div class="cn-overview-dialog__focus-main">
|
||||
<div class="cn-overview-dialog__focus-host">{{ item.host || "-" }}</div>
|
||||
<div class="cn-overview-dialog__focus-meta">
|
||||
{{ item.provider || "-" }} / 收录 {{ item.indexed_status || "-" }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="cn-overview-dialog__focus-side">
|
||||
<div>PC词数 {{ item.pc_keyword_count ?? "-" }}</div>
|
||||
<div>移动词数 {{ item.mobile_keyword_count ?? "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane-card">
|
||||
<div class="pane-card__title">当前优先动作</div>
|
||||
<div v-if="!(summary.top_suggestions || []).length" class="pane-card__empty">当前还没有自动建议</div>
|
||||
<div v-else class="cn-overview-dialog__focus-list">
|
||||
<div
|
||||
v-for="(item, index) in (summary.top_suggestions || []).slice(0, 3)"
|
||||
:key="`${item.title || 'suggestion'}-${index}`"
|
||||
class="cn-overview-dialog__focus-item"
|
||||
>
|
||||
<div class="cn-overview-dialog__focus-main">
|
||||
<div class="cn-overview-dialog__focus-host">{{ item.title || "-" }}</div>
|
||||
<div class="cn-overview-dialog__focus-meta">{{ item.summary || "-" }}</div>
|
||||
</div>
|
||||
<div class="cn-overview-dialog__focus-side cn-overview-dialog__focus-side--action">
|
||||
{{ item.action || "-" }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="16" class="cn-overview-dialog__panes">
|
||||
<el-col :span="12">
|
||||
<div class="pane-card">
|
||||
<div class="pane-card__title">重点 Host</div>
|
||||
<el-table v-loading="loading" :data="summary.top_hosts || []" border max-height="360">
|
||||
<el-table-column prop="host" label="Host" min-width="160" />
|
||||
<el-table-column prop="provider" label="Provider" width="100" />
|
||||
<el-table-column prop="indexed_status" label="收录状态" width="110" />
|
||||
<el-table-column prop="pc_keyword_count" label="PC词数" width="90" />
|
||||
<el-table-column prop="mobile_keyword_count" label="移动词数" width="90" />
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="pane-card">
|
||||
<div class="pane-card__title">优先建议</div>
|
||||
<el-table v-loading="loading" :data="summary.top_suggestions || []" border max-height="360">
|
||||
<el-table-column prop="priority" label="优先级" width="90" />
|
||||
<el-table-column prop="title" label="建议标题" min-width="160" />
|
||||
<el-table-column prop="action" label="建议动作" min-width="260" />
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { SiteList } from "@/api/interface/site/list";
|
||||
import { getDomainExternalSeoCnOverview } from "@/api/modules/site/list";
|
||||
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const summary = ref<SiteList.DomainExternalSeoCnOverviewData>({});
|
||||
|
||||
const loadSummary = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getDomainExternalSeoCnOverview({ days: 14, limit: 20 });
|
||||
summary.value = res.data ?? {};
|
||||
} catch (_error) {
|
||||
ElMessage.error("读取大陆 SEO 总览失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const buildHealthType = (healthLabel?: string) => {
|
||||
if (healthLabel === "growing") return "success";
|
||||
if (healthLabel === "attention" || healthLabel === "captcha") return "warning";
|
||||
return "info";
|
||||
};
|
||||
|
||||
const buildHealthLabel = (healthLabel?: string) => {
|
||||
if (healthLabel === "growing") return "增长中";
|
||||
if (healthLabel === "attention") return "需要关注";
|
||||
if (healthLabel === "captcha") return "抓取风控偏多";
|
||||
return healthLabel || "等待大盘数据";
|
||||
};
|
||||
|
||||
const buildHeroMetrics = (data: SiteList.DomainExternalSeoCnOverviewData) => {
|
||||
return [
|
||||
{
|
||||
label: "收录新增",
|
||||
value: `${Number(data.index_movement?.newly_indexed_count || 0)}`,
|
||||
},
|
||||
{
|
||||
label: "收录丢失",
|
||||
value: `${Number(data.index_movement?.lost_indexed_count || 0)}`,
|
||||
},
|
||||
{
|
||||
label: "关键词上升",
|
||||
value: `${Number(data.keyword_movement?.up_count || 0)}`,
|
||||
},
|
||||
{
|
||||
label: "关键词下降",
|
||||
value: `${Number(data.keyword_movement?.down_count || 0)}`,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const open = async () => {
|
||||
visible.value = true;
|
||||
await loadSummary();
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.cn-overview-dialog__toolbar { margin-bottom: 12px; }
|
||||
.cn-overview-dialog__hero {
|
||||
display:grid;
|
||||
grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);
|
||||
gap:12px;
|
||||
margin:16px 0;
|
||||
}
|
||||
.cn-overview-dialog__hero-main {
|
||||
border-radius:12px;
|
||||
padding:16px;
|
||||
color:#fff;
|
||||
background:linear-gradient(135deg, #14532d 0%, #1d4ed8 100%);
|
||||
}
|
||||
.cn-overview-dialog__hero-label { font-size:13px; opacity:.86; }
|
||||
.cn-overview-dialog__hero-value { margin-top:6px; font-size:28px; font-weight:700; }
|
||||
.cn-overview-dialog__hero-note { margin-top:8px; font-size:13px; line-height:1.6; opacity:.92; }
|
||||
.cn-overview-dialog__hero-metrics {
|
||||
display:grid;
|
||||
grid-template-columns:repeat(2,minmax(0,1fr));
|
||||
gap:12px;
|
||||
}
|
||||
.cn-overview-dialog__metrics { display:grid; grid-template-columns:repeat(6,minmax(120px,1fr)); gap:12px; margin:16px 0; }
|
||||
.cn-overview-dialog__metrics--priority { grid-template-columns:repeat(6,minmax(120px,1fr)); }
|
||||
.metric-card { border:1px solid #ebeef5; border-radius:12px; padding:12px; background:#fafafa; }
|
||||
.metric-card--hero { background:#fafafa; }
|
||||
.metric-card--priority-high { background:#fff1f0; border-color:#fecaca; }
|
||||
.metric-card--priority-medium { background:#fffbeb; border-color:#fde68a; }
|
||||
.metric-card--priority-low { background:#f0f9ff; border-color:#bae6fd; }
|
||||
.metric-card__label { color:#909399; font-size:12px; margin-bottom:6px; }
|
||||
.metric-card__value { color:#303133; font-size:22px; font-weight:600; }
|
||||
.cn-overview-dialog__descriptions { margin: 8px 0 16px; }
|
||||
.cn-overview-dialog__attention {
|
||||
display:grid;
|
||||
grid-template-columns:repeat(2,minmax(0,1fr));
|
||||
gap:16px;
|
||||
margin-bottom:16px;
|
||||
}
|
||||
.cn-overview-dialog__panes { margin-top: 16px; }
|
||||
.pane-card { border:1px solid #ebeef5; border-radius:12px; padding:12px; background:#fff; }
|
||||
.pane-card__title { font-weight:600; margin-bottom:12px; }
|
||||
.pane-card__empty { color:#909399; font-size:13px; }
|
||||
.cn-overview-dialog__focus-list { display:flex; flex-direction:column; gap:10px; }
|
||||
.cn-overview-dialog__focus-item {
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
gap:12px;
|
||||
padding:10px;
|
||||
border-radius:10px;
|
||||
background:#fafafa;
|
||||
}
|
||||
.cn-overview-dialog__focus-main { min-width:0; }
|
||||
.cn-overview-dialog__focus-host { color:#303133; font-weight:600; word-break:break-all; }
|
||||
.cn-overview-dialog__focus-meta { margin-top:6px; color:#606266; font-size:12px; line-height:1.5; }
|
||||
.cn-overview-dialog__focus-side { color:#606266; font-size:12px; line-height:1.8; text-align:right; }
|
||||
.cn-overview-dialog__focus-side--action { max-width:220px; text-align:left; }
|
||||
@media (max-width: 1100px) {
|
||||
.cn-overview-dialog__hero,
|
||||
.cn-overview-dialog__attention,
|
||||
.cn-overview-dialog__hero-metrics,
|
||||
.cn-overview-dialog__metrics,
|
||||
.cn-overview-dialog__metrics--priority {
|
||||
grid-template-columns:1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user