feat(analytics): 为访问和评论趋势图表添加7天/30天切换功能
- 在访问趋势和评论数趋势卡片标题栏添加切换按钮 - 重构图表数据源处理逻辑,支持按时间范围筛选 - 添加对应的CSS样式实现标签式切换器 - 保持默认显示最近30天数据,点击切换时重新渲染图表
This commit is contained in:
@@ -42,7 +42,27 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3 class="card-title">最近 30 天访问趋势</h3>
|
<div class="card-title-row">
|
||||||
|
<h3 class="card-title">访问趋势</h3>
|
||||||
|
<div class="visit-tabs">
|
||||||
|
<button
|
||||||
|
class="visit-tab"
|
||||||
|
:class="{ 'visit-tab-active': chartRange === '7' }"
|
||||||
|
type="button"
|
||||||
|
@click="changeChartRange('7')"
|
||||||
|
>
|
||||||
|
最近 7 天
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="visit-tab"
|
||||||
|
:class="{ 'visit-tab-active': chartRange === '30' }"
|
||||||
|
type="button"
|
||||||
|
@click="changeChartRange('30')"
|
||||||
|
>
|
||||||
|
最近 30 天
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-if="loading" class="page-hint">加载中...</div>
|
<div v-if="loading" class="page-hint">加载中...</div>
|
||||||
<div v-else-if="error" class="page-error">{{ error }}</div>
|
<div v-else-if="error" class="page-error">{{ error }}</div>
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
@@ -187,6 +207,7 @@ const injectedDomainFilter = inject<Ref<string> | null>("domainFilter", null);
|
|||||||
const domainFilter = injectedDomainFilter ?? ref("");
|
const domainFilter = injectedDomainFilter ?? ref("");
|
||||||
const last30Days = ref<{ date: string; total: number }[]>([]);
|
const last30Days = ref<{ date: string; total: number }[]>([]);
|
||||||
const likeStatsItems = ref<LikeStatsItem[]>([]);
|
const likeStatsItems = ref<LikeStatsItem[]>([]);
|
||||||
|
const chartRange = ref<"7" | "30">("30");
|
||||||
|
|
||||||
const toastMessage = ref("");
|
const toastMessage = ref("");
|
||||||
const toastType = ref<"success" | "error">("success");
|
const toastType = ref<"success" | "error">("success");
|
||||||
@@ -375,8 +396,11 @@ function renderChart() {
|
|||||||
if (!chartInstance) {
|
if (!chartInstance) {
|
||||||
chartInstance = echarts.init(el);
|
chartInstance = echarts.init(el);
|
||||||
}
|
}
|
||||||
const dates = last30Days.value.map((item) => item.date.slice(5));
|
const source = last30Days.value;
|
||||||
const values = last30Days.value.map((item) => item.total);
|
const seriesData =
|
||||||
|
chartRange.value === "7" ? source.slice(-7) : source;
|
||||||
|
const dates = seriesData.map((item) => item.date.slice(5));
|
||||||
|
const values = seriesData.map((item) => item.total);
|
||||||
const option: echarts.EChartsOption = {
|
const option: echarts.EChartsOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
@@ -422,6 +446,14 @@ function renderChart() {
|
|||||||
chartInstance.setOption(option);
|
chartInstance.setOption(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changeChartRange(range: "7" | "30") {
|
||||||
|
if (chartRange.value === range) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chartRange.value = range;
|
||||||
|
renderChart();
|
||||||
|
}
|
||||||
|
|
||||||
function handleResize() {
|
function handleResize() {
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.resize();
|
chartInstance.resize();
|
||||||
|
|||||||
@@ -42,7 +42,27 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3 class="card-title">最近 30 天评论数趋势</h3>
|
<div class="card-title-row">
|
||||||
|
<h3 class="card-title">评论数趋势</h3>
|
||||||
|
<div class="chart-tabs">
|
||||||
|
<button
|
||||||
|
class="chart-tab"
|
||||||
|
:class="{ 'chart-tab-active': chartRange === '7' }"
|
||||||
|
type="button"
|
||||||
|
@click="changeChartRange('7')"
|
||||||
|
>
|
||||||
|
最近 7 天
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="chart-tab"
|
||||||
|
:class="{ 'chart-tab-active': chartRange === '30' }"
|
||||||
|
type="button"
|
||||||
|
@click="changeChartRange('30')"
|
||||||
|
>
|
||||||
|
最近 30 天
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-if="statsLoading" class="page-hint">加载中...</div>
|
<div v-if="statsLoading" class="page-hint">加载中...</div>
|
||||||
<div v-else-if="statsError" class="page-error">{{ statsError }}</div>
|
<div v-else-if="statsError" class="page-error">{{ statsError }}</div>
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
@@ -105,6 +125,7 @@ const statsSummary = ref({
|
|||||||
});
|
});
|
||||||
const domainStats = ref<DomainStat[]>([]);
|
const domainStats = ref<DomainStat[]>([]);
|
||||||
const last7Days = ref<{ date: string; total: number }[]>([]);
|
const last7Days = ref<{ date: string; total: number }[]>([]);
|
||||||
|
const chartRange = ref<"7" | "30">("30");
|
||||||
|
|
||||||
const injectedDomainFilter = inject<Ref<string> | null>("domainFilter", null);
|
const injectedDomainFilter = inject<Ref<string> | null>("domainFilter", null);
|
||||||
const domainFilter = injectedDomainFilter ?? ref("");
|
const domainFilter = injectedDomainFilter ?? ref("");
|
||||||
@@ -159,8 +180,11 @@ function renderChart() {
|
|||||||
if (!chartInstance) {
|
if (!chartInstance) {
|
||||||
chartInstance = echarts.init(el);
|
chartInstance = echarts.init(el);
|
||||||
}
|
}
|
||||||
const dates = last7Days.value.map((item) => item.date.slice(5));
|
const source = last7Days.value;
|
||||||
const values = last7Days.value.map((item) => item.total);
|
const seriesData =
|
||||||
|
chartRange.value === "7" ? source.slice(-7) : source;
|
||||||
|
const dates = seriesData.map((item) => item.date.slice(5));
|
||||||
|
const values = seriesData.map((item) => item.total);
|
||||||
const option: echarts.EChartsOption = {
|
const option: echarts.EChartsOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
@@ -206,6 +230,14 @@ function renderChart() {
|
|||||||
chartInstance.setOption(option);
|
chartInstance.setOption(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changeChartRange(range: "7" | "30") {
|
||||||
|
if (chartRange.value === range) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chartRange.value = range;
|
||||||
|
renderChart();
|
||||||
|
}
|
||||||
|
|
||||||
function handleResize() {
|
function handleResize() {
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.resize();
|
chartInstance.resize();
|
||||||
@@ -277,6 +309,14 @@ onBeforeUnmount(() => {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.page-hint {
|
.page-hint {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #57606a;
|
color: #57606a;
|
||||||
@@ -287,6 +327,31 @@ onBeforeUnmount(() => {
|
|||||||
color: #d1242f;
|
color: #d1242f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart-tabs {
|
||||||
|
display: inline-flex;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid #d0d7de;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-tab {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
border: none;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #57606a;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-tab + .chart-tab {
|
||||||
|
border-left: 1px solid #d0d7de;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-tab-active {
|
||||||
|
background-color: #0969da;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
.stats-grid {
|
.stats-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
|||||||
Reference in New Issue
Block a user