|
|
@@ -0,0 +1,1024 @@
|
|
|
+<template>
|
|
|
+ <div class="table-box compare-page">
|
|
|
+ <DetailHeader title="对比分析" @back="goBack">
|
|
|
+ <template #right>
|
|
|
+ <a href="javascript:void(0)" class="ai-link" @click="openAiAnalysis">AI分析</a>
|
|
|
+ </template>
|
|
|
+ </DetailHeader>
|
|
|
+
|
|
|
+ <!-- AI 分析 - 右侧抽屉 -->
|
|
|
+ <el-drawer
|
|
|
+ v-model="aiDialogVisible"
|
|
|
+ title="AI分析"
|
|
|
+ direction="rtl"
|
|
|
+ size="420px"
|
|
|
+ class="ai-analysis-drawer"
|
|
|
+ :close-on-click-modal="true"
|
|
|
+ @close="onAiDialogClose"
|
|
|
+ >
|
|
|
+ <div class="ai-drawer-body">
|
|
|
+ <template v-if="aiDetailLoading || !aiAnalysisCompleted">
|
|
|
+ <div class="ai-analyzing">AI分析中</div>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <div class="ai-section">
|
|
|
+ <div class="ai-section-title">运营分析</div>
|
|
|
+ <div class="ai-section-content">
|
|
|
+ {{ aiSummary || "--" }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="ai-section">
|
|
|
+ <div class="ai-section-title">建议运营方案</div>
|
|
|
+ <div class="ai-section-content">
|
|
|
+ {{ aiOptimizationSuggestions || "--" }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+
|
|
|
+ <div class="content">
|
|
|
+ <!-- 日期 PK 展示 -->
|
|
|
+ <div class="date-pk-bar">
|
|
|
+ {{ dateRangeText }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 标签页(与数据概况等页一致的 el-card + el-tabs) -->
|
|
|
+ <el-card class="content-card" shadow="hover" v-loading="loading">
|
|
|
+ <el-tabs v-model="activeTab" class="data-tabs">
|
|
|
+ <el-tab-pane label="流量数据" name="traffic">
|
|
|
+ <div class="compare-cards">
|
|
|
+ <div v-for="item in trafficCompareList" :key="item.key" class="compare-card">
|
|
|
+ <div class="card-title">
|
|
|
+ {{ item.title }}
|
|
|
+ </div>
|
|
|
+ <div class="card-values">
|
|
|
+ <span class="current-value">{{ item.current }}</span>
|
|
|
+ <span class="compare-value">{{ item.compare }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-change" :class="item.trend">
|
|
|
+ <el-icon v-if="item.trend === 'up'">
|
|
|
+ <Top />
|
|
|
+ </el-icon>
|
|
|
+ <el-icon v-else>
|
|
|
+ <Bottom />
|
|
|
+ </el-icon>
|
|
|
+ <span>{{ item.changeText }} 较{{ compareLabel }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-chart" :class="item.trend">
|
|
|
+ <svg viewBox="0 0 60 24" class="mini-chart">
|
|
|
+ <polyline
|
|
|
+ v-if="item.trend === 'up'"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.5"
|
|
|
+ :points="item.chartPoints"
|
|
|
+ />
|
|
|
+ <polyline v-else fill="none" stroke="currentColor" stroke-width="1.5" :points="item.chartPoints" />
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="互动数据" name="interaction">
|
|
|
+ <div class="compare-cards">
|
|
|
+ <div v-for="item in interactionCompareList" :key="item.key" class="compare-card">
|
|
|
+ <div class="card-title">
|
|
|
+ {{ item.title }}
|
|
|
+ </div>
|
|
|
+ <div class="card-values">
|
|
|
+ <span class="current-value">{{ item.current }}</span>
|
|
|
+ <span class="compare-value">{{ item.compare }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-change" :class="item.trend">
|
|
|
+ <el-icon v-if="item.trend === 'up'">
|
|
|
+ <Top />
|
|
|
+ </el-icon>
|
|
|
+ <el-icon v-else>
|
|
|
+ <Bottom />
|
|
|
+ </el-icon>
|
|
|
+ <span>{{ item.changeText }} 较{{ compareLabel }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-chart" :class="item.trend">
|
|
|
+ <svg viewBox="0 0 60 24" class="mini-chart">
|
|
|
+ <polyline
|
|
|
+ v-if="item.trend === 'up'"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.5"
|
|
|
+ :points="item.chartPoints"
|
|
|
+ />
|
|
|
+ <polyline v-else fill="none" stroke="currentColor" stroke-width="1.5" :points="item.chartPoints" />
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="优惠券" name="coupon">
|
|
|
+ <div class="compare-cards">
|
|
|
+ <div v-for="item in couponCompareList" :key="item.key" class="compare-card">
|
|
|
+ <div class="card-title">
|
|
|
+ {{ item.title }}
|
|
|
+ </div>
|
|
|
+ <div class="card-values">
|
|
|
+ <span class="current-value">{{ item.current }}</span>
|
|
|
+ <span class="compare-value">{{ item.compare }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-change" :class="item.trend">
|
|
|
+ <el-icon v-if="item.trend === 'up'">
|
|
|
+ <Top />
|
|
|
+ </el-icon>
|
|
|
+ <el-icon v-else>
|
|
|
+ <Bottom />
|
|
|
+ </el-icon>
|
|
|
+ <span>{{ item.changeText }} 较{{ compareLabel }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-chart" :class="item.trend">
|
|
|
+ <svg viewBox="0 0 60 24" class="mini-chart">
|
|
|
+ <polyline
|
|
|
+ v-if="item.trend === 'up'"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.5"
|
|
|
+ :points="item.chartPoints"
|
|
|
+ />
|
|
|
+ <polyline v-else fill="none" stroke="currentColor" stroke-width="1.5" :points="item.chartPoints" />
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="代金券" name="voucher">
|
|
|
+ <div class="compare-cards">
|
|
|
+ <div v-for="item in voucherCompareList" :key="item.key" class="compare-card">
|
|
|
+ <div class="card-title">
|
|
|
+ {{ item.title }}
|
|
|
+ </div>
|
|
|
+ <div class="card-values">
|
|
|
+ <span class="current-value">{{ item.current }}</span>
|
|
|
+ <span class="compare-value">{{ item.compare }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-change" :class="item.trend">
|
|
|
+ <el-icon v-if="item.trend === 'up'">
|
|
|
+ <Top />
|
|
|
+ </el-icon>
|
|
|
+ <el-icon v-else>
|
|
|
+ <Bottom />
|
|
|
+ </el-icon>
|
|
|
+ <span>{{ item.changeText }} 较{{ compareLabel }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-chart" :class="item.trend">
|
|
|
+ <svg viewBox="0 0 60 24" class="mini-chart">
|
|
|
+ <polyline
|
|
|
+ v-if="item.trend === 'up'"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.5"
|
|
|
+ :points="item.chartPoints"
|
|
|
+ />
|
|
|
+ <polyline v-else fill="none" stroke="currentColor" stroke-width="1.5" :points="item.chartPoints" />
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="服务质量" name="service">
|
|
|
+ <div class="compare-cards">
|
|
|
+ <div v-for="item in serviceCompareList" :key="item.key" class="compare-card">
|
|
|
+ <div class="card-title">
|
|
|
+ {{ item.title }}
|
|
|
+ </div>
|
|
|
+ <div class="card-values">
|
|
|
+ <span class="current-value">{{ item.current }}</span>
|
|
|
+ <span class="compare-value">{{ item.compare }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-change" :class="item.trend">
|
|
|
+ <el-icon v-if="item.trend === 'up'" size="12">
|
|
|
+ <Top />
|
|
|
+ </el-icon>
|
|
|
+ <el-icon v-else-if="item.trend === 'down'" size="12">
|
|
|
+ <Bottom />
|
|
|
+ </el-icon>
|
|
|
+ <el-icon v-else size="12">
|
|
|
+ <Right />
|
|
|
+ </el-icon>
|
|
|
+ <span>{{ item.changeText }} 较{{ compareLabel }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-chart" :class="item.trend">
|
|
|
+ <svg viewBox="0 0 60 24" class="mini-chart">
|
|
|
+ <polyline
|
|
|
+ v-if="item.trend === 'up'"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.5"
|
|
|
+ :points="item.chartPoints"
|
|
|
+ />
|
|
|
+ <polyline v-else fill="none" stroke="currentColor" stroke-width="1.5" :points="item.chartPoints" />
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="价目表排名" name="ranking">
|
|
|
+ <div class="compare-cards">
|
|
|
+ <table class="price-list-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>排名</th>
|
|
|
+ <th>价目表名称</th>
|
|
|
+ <th>浏览量</th>
|
|
|
+ <th>访客</th>
|
|
|
+ <th>分享数</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="(item, index) in priceListRankingData" :key="item.priceId">
|
|
|
+ <td>{{ index + 1 }}</td>
|
|
|
+ <td>{{ item.priceListItemName }}</td>
|
|
|
+ <td>
|
|
|
+ <div class="value-group">
|
|
|
+ <span class="current-value">{{ item.pageViews.current }}</span>
|
|
|
+ <span class="compare-value">{{ item.pageViews.previous }}</span>
|
|
|
+ <span class="change-value" :class="item.pageViews.changeRate >= 0 ? 'up' : 'down'">
|
|
|
+ {{ item.pageViews.changeRate >= 0 ? "↑" : "↓" }}{{ Math.abs(item.pageViews.changeRate) }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </td>
|
|
|
+ <td>
|
|
|
+ <div class="value-group">
|
|
|
+ <span class="current-value">{{ item.visitors.current }}</span>
|
|
|
+ <span class="compare-value">{{ item.visitors.previous }}</span>
|
|
|
+ <span class="change-value" :class="item.visitors.changeRate >= 0 ? 'up' : 'down'">
|
|
|
+ {{ item.visitors.changeRate >= 0 ? "↑" : "↓" }}{{ Math.abs(item.visitors.changeRate) }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </td>
|
|
|
+ <td>
|
|
|
+ <div class="value-group">
|
|
|
+ <span class="current-value">{{ item.shares.current }}</span>
|
|
|
+ <span class="compare-value">{{ item.shares.previous }}</span>
|
|
|
+ <span class="change-value" :class="item.shares.changeRate >= 0 ? 'up' : 'down'">
|
|
|
+ {{ item.shares.changeRate >= 0 ? "↑" : "↓" }}{{ Math.abs(item.shares.changeRate) }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts" name="businessDataCompare">
|
|
|
+import { ref, computed, onMounted } from "vue";
|
|
|
+import { useRouter, useRoute } from "vue-router";
|
|
|
+import { Top, Bottom } from "@element-plus/icons-vue";
|
|
|
+import { ElMessage } from "element-plus";
|
|
|
+import { localGet } from "@/utils";
|
|
|
+import DetailHeader from "./components/DetailHeader.vue";
|
|
|
+import { getStatisticsComparison, getHistoryDetail } from "@/api/modules/businessData";
|
|
|
+import type { TrafficData } from "@/api/modules/businessData";
|
|
|
+
|
|
|
+const router = useRouter();
|
|
|
+const route = useRoute();
|
|
|
+const activeTab = ref("traffic");
|
|
|
+const loading = ref(false);
|
|
|
+// 对比接口返回的 historyId,用于 AI 分析详情
|
|
|
+const comparisonHistoryId = ref<number | undefined>(undefined);
|
|
|
+// AI 分析弹窗
|
|
|
+const aiDialogVisible = ref(false);
|
|
|
+const aiDetailLoading = ref(false);
|
|
|
+const aiSummary = ref("");
|
|
|
+const aiOptimizationSuggestions = ref("");
|
|
|
+const aiAnalysisCompleted = ref(0);
|
|
|
+
|
|
|
+// 从路由 query 取日期与对比类型
|
|
|
+const queryStart = (route.query.start as string) || "";
|
|
|
+const queryEnd = (route.query.end as string) || "";
|
|
|
+const queryCompareStart = (route.query.compareStart as string) || "";
|
|
|
+const queryCompareEnd = (route.query.compareEnd as string) || "";
|
|
|
+const compareType = (route.query.compareType as string) || "lastPeriod";
|
|
|
+
|
|
|
+const compareLabel = computed(() => (compareType === "samePeriod" ? "同期" : "上期"));
|
|
|
+
|
|
|
+const dateRangeText = computed(() => {
|
|
|
+ if (!queryStart || !queryEnd) return "2026/01/07-2026/01/08 PK 2025/01/07-2025/01/08";
|
|
|
+ const currentStr = `${queryStart.replace(/-/g, "/")}-${queryEnd.replace(/-/g, "/")}`;
|
|
|
+
|
|
|
+ // 优先使用概况页传入的 compareStart/compareEnd
|
|
|
+ if (queryCompareStart && queryCompareEnd) {
|
|
|
+ const otherStr = `${queryCompareStart.replace(/-/g, "/")}-${queryCompareEnd.replace(/-/g, "/")}`;
|
|
|
+ return `${currentStr} PK ${otherStr}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 兜底:根据开始日期自动算上一年同月同日
|
|
|
+ const year = parseInt(queryStart.slice(0, 4), 10);
|
|
|
+ const otherStart = `${year - 1}${queryStart.slice(4)}`.replace(/-/g, "/");
|
|
|
+ const otherEnd = `${year - 1}${queryEnd.slice(4)}`.replace(/-/g, "/");
|
|
|
+ return `${currentStr} PK ${otherStart}-${otherEnd}`;
|
|
|
+});
|
|
|
+
|
|
|
+function goBack() {
|
|
|
+ router.back();
|
|
|
+}
|
|
|
+
|
|
|
+// 打开 AI 分析弹窗并拉取详情
|
|
|
+async function openAiAnalysis() {
|
|
|
+ aiDialogVisible.value = true;
|
|
|
+ aiDetailLoading.value = true;
|
|
|
+ aiSummary.value = "";
|
|
|
+ aiOptimizationSuggestions.value = "";
|
|
|
+ aiAnalysisCompleted.value = 0;
|
|
|
+
|
|
|
+ const historyId = comparisonHistoryId.value;
|
|
|
+ if (historyId == null || historyId === undefined) {
|
|
|
+ aiDetailLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res: any = await getHistoryDetail({ id: historyId });
|
|
|
+ const data = res?.data ?? res;
|
|
|
+ const completed = data?.aiAnalysisCompleted;
|
|
|
+ const hasCompleted = completed === 1;
|
|
|
+ aiAnalysisCompleted.value = hasCompleted ? 1 : 0;
|
|
|
+ if (hasCompleted) {
|
|
|
+ aiSummary.value = data?.summary ?? "";
|
|
|
+ aiOptimizationSuggestions.value = data?.optimizationSuggestions ?? "";
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ aiAnalysisCompleted.value = 0;
|
|
|
+ } finally {
|
|
|
+ aiDetailLoading.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function onAiDialogClose() {
|
|
|
+ aiSummary.value = "";
|
|
|
+ aiOptimizationSuggestions.value = "";
|
|
|
+ aiAnalysisCompleted.value = 0;
|
|
|
+}
|
|
|
+
|
|
|
+// 秒 -> XhXmXs 或 XmXs
|
|
|
+function formatDuration(seconds: number | undefined): string {
|
|
|
+ if (seconds == null || isNaN(Number(seconds))) return "--";
|
|
|
+ const n = Math.floor(Number(seconds));
|
|
|
+ if (n < 60) return `${n}s`;
|
|
|
+ const m = Math.floor(n / 60);
|
|
|
+ const s = n % 60;
|
|
|
+ if (m < 60) return `${m}m${s}s`;
|
|
|
+ const h = Math.floor(m / 60);
|
|
|
+ const mm = m % 60;
|
|
|
+ return `${h}h${mm}m${s}s`;
|
|
|
+}
|
|
|
+
|
|
|
+// 数字或占比展示
|
|
|
+function formatNum(val: number | undefined): string {
|
|
|
+ if (val == null || (typeof val === "number" && isNaN(val))) return "--";
|
|
|
+ return String(val);
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化百分比
|
|
|
+function formatPercent(percent: number | undefined): string {
|
|
|
+ if (percent == null || (typeof percent === "number" && isNaN(percent))) return "--";
|
|
|
+ return `${Number(percent).toFixed(2)}%`;
|
|
|
+}
|
|
|
+
|
|
|
+// 数据相同时为直线,否则为趋势折线(viewBox 0 0 60 24)
|
|
|
+function getChartPoints(changeVal: number): string {
|
|
|
+ if (changeVal === 0) return "2,12 58,12";
|
|
|
+ return changeVal > 0 ? "2,20 15,14 30,8 45,4 58,2" : "2,4 18,8 35,14 50,18 58,20";
|
|
|
+}
|
|
|
+
|
|
|
+// 对比数据项类型
|
|
|
+interface CompareItem {
|
|
|
+ key: string;
|
|
|
+ title: string;
|
|
|
+ current: string;
|
|
|
+ compare: string;
|
|
|
+ trend: "up" | "down";
|
|
|
+ changeText: string;
|
|
|
+ chartPoints: string;
|
|
|
+}
|
|
|
+
|
|
|
+const trafficCompareList = ref<CompareItem[]>([]);
|
|
|
+// 互动数据对比
|
|
|
+const interactionCompareList = ref<CompareItem[]>([]);
|
|
|
+// 优惠券数据对比
|
|
|
+const couponCompareList = ref<CompareItem[]>([]);
|
|
|
+// 代金券数据对比
|
|
|
+const voucherCompareList = ref<CompareItem[]>([]);
|
|
|
+// 服务质量数据对比
|
|
|
+const serviceCompareList = ref<CompareItem[]>([]);
|
|
|
+// 价目表排名数据
|
|
|
+const priceListRankingData = ref<any[]>([]);
|
|
|
+
|
|
|
+// 设置流量数据对比
|
|
|
+function setTrafficCompare(trafficData: any | undefined) {
|
|
|
+ const trafficItems = [
|
|
|
+ { key: "storeSearchVolume", title: "店铺搜索量" },
|
|
|
+ { key: "pageViews", title: "浏览量" },
|
|
|
+ { key: "visitors", title: "访客数" },
|
|
|
+ { key: "newVisitors", title: "新增访客数" },
|
|
|
+ { key: "visitDuration", title: "访问时长", formatter: formatDuration },
|
|
|
+ { key: "avgVisitDuration", title: "平均访问时长", formatter: formatDuration }
|
|
|
+ ];
|
|
|
+
|
|
|
+ trafficCompareList.value = trafficItems.map(item => {
|
|
|
+ const itemData = trafficData ? (trafficData as any)[item.key] : undefined;
|
|
|
+ const currentVal = itemData?.current;
|
|
|
+ const previousVal = itemData?.previous;
|
|
|
+ const changeVal = itemData?.changeRate || 0;
|
|
|
+ const formatter = item.formatter || formatNum;
|
|
|
+
|
|
|
+ return {
|
|
|
+ key: item.key,
|
|
|
+ title: item.title,
|
|
|
+ current: formatter(currentVal),
|
|
|
+ compare: formatter(previousVal),
|
|
|
+ trend: changeVal >= 0 ? "up" : "down",
|
|
|
+ changeText: changeVal >= 0 ? `↑${Math.abs(changeVal)}` : `↓${Math.abs(changeVal)}`,
|
|
|
+ chartPoints: getChartPoints(changeVal)
|
|
|
+ };
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 设置互动数据对比
|
|
|
+function setInteractionCompare(interactionData: any | undefined) {
|
|
|
+ const interactionItems = [
|
|
|
+ { key: "storeCollectionCount", title: "店铺收藏次数" },
|
|
|
+ { key: "storeShareCount", title: "店铺分享次数" },
|
|
|
+ { key: "storeCheckInCount", title: "店铺打卡次数" },
|
|
|
+ { key: "consultMerchantCount", title: "咨询商家次数" },
|
|
|
+ { key: "friendsCount", title: "好友数量" },
|
|
|
+ { key: "followCount", title: "关注数量" },
|
|
|
+ { key: "fansCount", title: "粉丝数量" },
|
|
|
+ { key: "postsPublishedCount", title: "发布动态数量" },
|
|
|
+ { key: "postLikesCount", title: "动态点赞数量" },
|
|
|
+ { key: "postCommentsCount", title: "动态评论数量" },
|
|
|
+ { key: "postSharesCount", title: "动态转发数量" },
|
|
|
+ { key: "reportedCount", title: "被举报次数" },
|
|
|
+ { key: "blockedCount", title: "被拉黑次数" }
|
|
|
+ ];
|
|
|
+
|
|
|
+ interactionCompareList.value = interactionItems.map(item => {
|
|
|
+ const itemData = interactionData ? (interactionData as any)[item.key] : undefined;
|
|
|
+ const currentVal = itemData?.current;
|
|
|
+ const previousVal = itemData?.previous;
|
|
|
+ const changeVal = itemData?.changeRate || 0;
|
|
|
+ const formatter = formatNum;
|
|
|
+
|
|
|
+ return {
|
|
|
+ key: item.key,
|
|
|
+ title: item.title,
|
|
|
+ current: formatter(currentVal),
|
|
|
+ compare: formatter(previousVal),
|
|
|
+ trend: changeVal >= 0 ? "up" : "down",
|
|
|
+ changeText: changeVal >= 0 ? `↑${Math.abs(changeVal)}` : `↓${Math.abs(changeVal)}`,
|
|
|
+ chartPoints: getChartPoints(changeVal)
|
|
|
+ };
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 设置优惠券数据对比
|
|
|
+function setCouponCompare(couponData: any | undefined) {
|
|
|
+ const couponItems = [
|
|
|
+ { key: "giftToFriendsCount", title: "赠送好友数量" },
|
|
|
+ { key: "giftToFriendsAmount", title: "赠送好友金额合计" },
|
|
|
+ { key: "giftToFriendsUsedCount", title: "赠送好友使用数量" },
|
|
|
+ { key: "giftToFriendsUsedAmount", title: "赠送好友使用金额合计" },
|
|
|
+ { key: "giftToFriendsUsedAmountRatio", title: "赠送好友使用金额占比", formatter: formatPercent },
|
|
|
+ { key: "friendsGiftCount", title: "好友赠送数量" },
|
|
|
+ { key: "friendsGiftAmount", title: "好友赠送金额合计" },
|
|
|
+ { key: "friendsGiftUsedCount", title: "好友赠送使用数量" },
|
|
|
+ { key: "friendsGiftUsedAmount", title: "好友赠送使用金额合计" },
|
|
|
+ { key: "friendsGiftUsedAmountRatio", title: "好友赠送使用金额占比", formatter: formatPercent }
|
|
|
+ ];
|
|
|
+
|
|
|
+ couponCompareList.value = couponItems.map(item => {
|
|
|
+ const itemData = couponData ? (couponData as any)[item.key] : undefined;
|
|
|
+ const currentVal = itemData?.current;
|
|
|
+ const previousVal = itemData?.previous;
|
|
|
+ const changeVal = itemData?.changeRate || 0;
|
|
|
+ const formatter = item.formatter || formatNum;
|
|
|
+
|
|
|
+ return {
|
|
|
+ key: item.key,
|
|
|
+ title: item.title,
|
|
|
+ current: formatter(currentVal),
|
|
|
+ compare: formatter(previousVal),
|
|
|
+ trend: changeVal >= 0 ? "up" : "down",
|
|
|
+ changeText: changeVal >= 0 ? `↑${Math.abs(changeVal)}` : `↓${Math.abs(changeVal)}`,
|
|
|
+ chartPoints: getChartPoints(changeVal)
|
|
|
+ };
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 设置代金券数据对比
|
|
|
+function setVoucherCompare(voucherData: any | undefined) {
|
|
|
+ const voucherItems = [
|
|
|
+ { key: "giftToFriendsCount", title: "赠送好友数量" },
|
|
|
+ { key: "giftToFriendsAmount", title: "赠送好友金额合计" },
|
|
|
+ { key: "giftToFriendsUsedCount", title: "赠送好友使用数量" },
|
|
|
+ { key: "giftToFriendsUsedAmount", title: "赠送好友使用金额合计" },
|
|
|
+ { key: "giftToFriendsUsedAmountRatio", title: "赠送好友使用金额占比", formatter: formatPercent },
|
|
|
+ { key: "friendsGiftCount", title: "好友赠送数量" },
|
|
|
+ { key: "friendsGiftAmount", title: "好友赠送金额合计" },
|
|
|
+ { key: "friendsGiftUsedCount", title: "好友赠送使用数量" },
|
|
|
+ { key: "friendsGiftUsedAmount", title: "好友赠送使用金额合计" },
|
|
|
+ { key: "friendsGiftUsedAmountRatio", title: "好友赠送使用金额占比", formatter: formatPercent }
|
|
|
+ ];
|
|
|
+
|
|
|
+ voucherCompareList.value = voucherItems.map(item => {
|
|
|
+ const itemData = voucherData ? (voucherData as any)[item.key] : undefined;
|
|
|
+ const currentVal = itemData?.current;
|
|
|
+ const previousVal = itemData?.previous;
|
|
|
+ const changeVal = itemData?.changeRate || 0;
|
|
|
+ const formatter = item.formatter || formatNum;
|
|
|
+
|
|
|
+ return {
|
|
|
+ key: item.key,
|
|
|
+ title: item.title,
|
|
|
+ current: formatter(currentVal),
|
|
|
+ compare: formatter(previousVal),
|
|
|
+ trend: changeVal >= 0 ? "up" : "down",
|
|
|
+ changeText: changeVal >= 0 ? `↑${Math.abs(changeVal)}` : `↓${Math.abs(changeVal)}`,
|
|
|
+ chartPoints: getChartPoints(changeVal)
|
|
|
+ };
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 设置服务质量数据对比
|
|
|
+function setServiceCompare(serviceData: any | undefined) {
|
|
|
+ const serviceItems = [
|
|
|
+ { key: "storeRating", title: "店铺评分" },
|
|
|
+ { key: "scoreOne", title: "口味评分" },
|
|
|
+ { key: "scoreTwo", title: "环境评分" },
|
|
|
+ { key: "totalReviews", title: "评价数量" },
|
|
|
+ { key: "positiveReviews", title: "好评数量" },
|
|
|
+ { key: "neutralReviews", title: "中评数量" },
|
|
|
+ { key: "negativeReviews", title: "差评数量" },
|
|
|
+ { key: "negativeReviewRatio", title: "差评占比", formatter: formatPercent },
|
|
|
+ { key: "negativeReviewAppealsCount", title: "差评申诉次数" },
|
|
|
+ { key: "negativeReviewAppealsSuccessCount", title: "差评申诉成功次数" },
|
|
|
+ { key: "negativeReviewAppealsSuccessRatio", title: "差评申诉成功占比", formatter: formatPercent }
|
|
|
+ ];
|
|
|
+
|
|
|
+ serviceCompareList.value = serviceItems.map(item => {
|
|
|
+ const itemData = serviceData ? (serviceData as any)[item.key] : undefined;
|
|
|
+ const currentVal = itemData?.current;
|
|
|
+ const previousVal = itemData?.previous;
|
|
|
+ const changeVal = itemData?.changeRate || 0;
|
|
|
+ const formatter = item.formatter || formatNum;
|
|
|
+
|
|
|
+ return {
|
|
|
+ key: item.key,
|
|
|
+ title: item.title,
|
|
|
+ current: formatter(currentVal),
|
|
|
+ compare: formatter(previousVal),
|
|
|
+ trend: changeVal >= 0 ? "up" : "down",
|
|
|
+ changeText: changeVal >= 0 ? `↑${Math.abs(changeVal)}` : `↓${Math.abs(changeVal)}`,
|
|
|
+ chartPoints: getChartPoints(changeVal)
|
|
|
+ };
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 获取对比数据
|
|
|
+async function fetchComparisonData() {
|
|
|
+ const storeId = localGet("createdId");
|
|
|
+ if (!storeId) {
|
|
|
+ ElMessage.warning("请先选择门店");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!queryStart || !queryEnd || !queryCompareStart || !queryCompareEnd) {
|
|
|
+ ElMessage.warning("日期参数不完整");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ console.log("开始获取对比数据", {
|
|
|
+ currentStartTime: queryStart,
|
|
|
+ currentEndTime: queryEnd,
|
|
|
+ previousStartTime: queryCompareStart,
|
|
|
+ previousEndTime: queryCompareEnd,
|
|
|
+ storeId
|
|
|
+ });
|
|
|
+
|
|
|
+ const res: any = await getStatisticsComparison({
|
|
|
+ currentStartTime: queryStart,
|
|
|
+ currentEndTime: queryEnd,
|
|
|
+ previousStartTime: queryCompareStart,
|
|
|
+ previousEndTime: queryCompareEnd,
|
|
|
+ storeId
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log("获取对比数据成功", res);
|
|
|
+
|
|
|
+ const data = res?.data ?? res;
|
|
|
+ comparisonHistoryId.value = data?.historyId;
|
|
|
+
|
|
|
+ console.log("接口返回的流量数据", data?.trafficData);
|
|
|
+ console.log("接口返回的互动数据", data?.interactionData);
|
|
|
+ console.log("接口返回的优惠券数据", data?.couponData);
|
|
|
+ console.log("接口返回的代金券数据", data?.voucherData);
|
|
|
+ console.log("接口返回的服务质量数据", data?.serviceQualityData);
|
|
|
+ console.log("接口返回的价目表排名数据", data?.priceListRanking);
|
|
|
+
|
|
|
+ // 设置流量数据对比
|
|
|
+ setTrafficCompare(data?.trafficData);
|
|
|
+ // 设置互动数据对比
|
|
|
+ setInteractionCompare(data?.interactionData);
|
|
|
+ // 设置优惠券数据对比
|
|
|
+ setCouponCompare(data?.couponData);
|
|
|
+ // 设置代金券数据对比
|
|
|
+ setVoucherCompare(data?.voucherData);
|
|
|
+ // 设置服务质量数据对比
|
|
|
+ setServiceCompare(data?.serviceQualityData);
|
|
|
+ // 设置价目表排名数据
|
|
|
+ priceListRankingData.value = data?.priceListRanking || [];
|
|
|
+
|
|
|
+ console.log("设置流量数据对比完成", trafficCompareList.value);
|
|
|
+ console.log("设置互动数据对比完成", interactionCompareList.value);
|
|
|
+ console.log("设置优惠券数据对比完成", couponCompareList.value);
|
|
|
+ console.log("设置代金券数据对比完成", voucherCompareList.value);
|
|
|
+ console.log("设置服务质量数据对比完成", serviceCompareList.value);
|
|
|
+ console.log("设置价目表排名数据完成", priceListRankingData.value);
|
|
|
+ } catch (e) {
|
|
|
+ console.error("获取对比数据失败", e);
|
|
|
+ ElMessage.error("获取对比数据失败");
|
|
|
+
|
|
|
+ // 模拟数据,用于测试显示效果
|
|
|
+ setTrafficCompare({
|
|
|
+ storeSearchVolume: { current: 1024, previous: 1111, changeRate: 112 },
|
|
|
+ pageViews: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ visitors: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ newVisitors: { current: 1024, previous: 1030, changeRate: 112 },
|
|
|
+ visitDuration: { current: 37413, previous: 37413, changeRate: 0 },
|
|
|
+ avgVisitDuration: { current: 93, previous: 33, changeRate: -6 }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 模拟互动数据,用于测试显示效果
|
|
|
+ setInteractionCompare({
|
|
|
+ storeCollectionCount: { current: 1024, previous: 1111, changeRate: 112 },
|
|
|
+ storeShareCount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ storeCheckInCount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ consultMerchantCount: { current: 1024, previous: 1030, changeRate: 112 },
|
|
|
+ friendsCount: { current: 1024, previous: 1111, changeRate: 112 },
|
|
|
+ followCount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ fansCount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ postsPublishedCount: { current: 1024, previous: 1030, changeRate: 112 },
|
|
|
+ postLikesCount: { current: 1024, previous: 1111, changeRate: 112 },
|
|
|
+ postCommentsCount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ postSharesCount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ reportedCount: { current: 1024, previous: 1030, changeRate: 112 },
|
|
|
+ blockedCount: { current: 1024, previous: 1111, changeRate: 112 }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 模拟优惠券数据,用于测试显示效果
|
|
|
+ setCouponCompare({
|
|
|
+ giftToFriendsCount: { current: 1024, previous: 1111, changeRate: 112 },
|
|
|
+ giftToFriendsAmount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ giftToFriendsUsedCount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ giftToFriendsUsedAmount: { current: 1024, previous: 1030, changeRate: 112 },
|
|
|
+ giftToFriendsUsedAmountRatio: { current: 15.21, previous: 13.21, changeRate: 2 },
|
|
|
+ friendsGiftCount: { current: 1024, previous: 1111, changeRate: 112 },
|
|
|
+ friendsGiftAmount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ friendsGiftUsedCount: { current: 1024, previous: 1030, changeRate: -6 },
|
|
|
+ friendsGiftUsedAmount: { current: 1024, previous: 1030, changeRate: 112 },
|
|
|
+ friendsGiftUsedAmountRatio: { current: 15.21, previous: 13.21, changeRate: 2 }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 模拟代金券数据,用于测试显示效果
|
|
|
+ setVoucherCompare({
|
|
|
+ giftToFriendsCount: { current: 512, previous: 600, changeRate: -88 },
|
|
|
+ giftToFriendsAmount: { current: 512, previous: 550, changeRate: -38 },
|
|
|
+ giftToFriendsUsedCount: { current: 512, previous: 550, changeRate: -38 },
|
|
|
+ giftToFriendsUsedAmount: { current: 512, previous: 550, changeRate: -38 },
|
|
|
+ giftToFriendsUsedAmountRatio: { current: 10.5, previous: 8.5, changeRate: 2 },
|
|
|
+ friendsGiftCount: { current: 512, previous: 600, changeRate: -88 },
|
|
|
+ friendsGiftAmount: { current: 512, previous: 550, changeRate: -38 },
|
|
|
+ friendsGiftUsedCount: { current: 512, previous: 550, changeRate: -38 },
|
|
|
+ friendsGiftUsedAmount: { current: 512, previous: 550, changeRate: -38 },
|
|
|
+ friendsGiftUsedAmountRatio: { current: 10.5, previous: 8.5, changeRate: 2 }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 模拟服务质量数据,用于测试显示效果
|
|
|
+ setServiceCompare({
|
|
|
+ storeRating: { current: 4.5, previous: 4.2, changeRate: 0.3 },
|
|
|
+ scoreOne: { current: 4.6, previous: 4.3, changeRate: 0.3 },
|
|
|
+ scoreTwo: { current: 4.4, previous: 4.1, changeRate: 0.3 },
|
|
|
+ totalReviews: { current: 1000, previous: 900, changeRate: 100 },
|
|
|
+ positiveReviews: { current: 800, previous: 700, changeRate: 100 },
|
|
|
+ neutralReviews: { current: 150, previous: 160, changeRate: -10 },
|
|
|
+ negativeReviews: { current: 50, previous: 40, changeRate: 10 },
|
|
|
+ negativeReviewRatio: { current: 5.0, previous: 4.44, changeRate: 0.56 },
|
|
|
+ negativeReviewAppealsCount: { current: 10, previous: 8, changeRate: 2 },
|
|
|
+ negativeReviewAppealsSuccessCount: { current: 8, previous: 6, changeRate: 2 },
|
|
|
+ negativeReviewAppealsSuccessRatio: { current: 80.0, previous: 75.0, changeRate: 5.0 }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 模拟价目表排名数据,用于测试显示效果
|
|
|
+ priceListRankingData.value = [
|
|
|
+ {
|
|
|
+ priceId: 1,
|
|
|
+ priceListItemName: "锅包肉",
|
|
|
+ pageViews: {
|
|
|
+ current: 2345,
|
|
|
+ previous: 2345,
|
|
|
+ changeRate: 0.5
|
|
|
+ },
|
|
|
+ visitors: {
|
|
|
+ current: 2789,
|
|
|
+ previous: 2789,
|
|
|
+ changeRate: 0.5
|
|
|
+ },
|
|
|
+ shares: {
|
|
|
+ current: 234,
|
|
|
+ previous: 234,
|
|
|
+ changeRate: 0.5
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ priceId: 2,
|
|
|
+ priceListItemName: "锅包肉",
|
|
|
+ pageViews: {
|
|
|
+ current: 2345,
|
|
|
+ previous: 2345,
|
|
|
+ changeRate: 0.5
|
|
|
+ },
|
|
|
+ visitors: {
|
|
|
+ current: 2789,
|
|
|
+ previous: 2789,
|
|
|
+ changeRate: 0.5
|
|
|
+ },
|
|
|
+ shares: {
|
|
|
+ current: 234,
|
|
|
+ previous: 234,
|
|
|
+ changeRate: 0.5
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ priceId: 3,
|
|
|
+ priceListItemName: "锅包肉",
|
|
|
+ pageViews: {
|
|
|
+ current: 2345,
|
|
|
+ previous: 2345,
|
|
|
+ changeRate: 0.5
|
|
|
+ },
|
|
|
+ visitors: {
|
|
|
+ current: 2789,
|
|
|
+ previous: 2789,
|
|
|
+ changeRate: 0.5
|
|
|
+ },
|
|
|
+ shares: {
|
|
|
+ current: 234,
|
|
|
+ previous: 234,
|
|
|
+ changeRate: 0.5
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ priceId: 4,
|
|
|
+ priceListItemName: "锅包肉",
|
|
|
+ pageViews: {
|
|
|
+ current: 2345,
|
|
|
+ previous: 2345,
|
|
|
+ changeRate: -6
|
|
|
+ },
|
|
|
+ visitors: {
|
|
|
+ current: 2789,
|
|
|
+ previous: 2789,
|
|
|
+ changeRate: -6
|
|
|
+ },
|
|
|
+ shares: {
|
|
|
+ current: 234,
|
|
|
+ previous: 234,
|
|
|
+ changeRate: -6
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 页面加载时获取数据
|
|
|
+onMounted(() => {
|
|
|
+ fetchComparisonData();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+/* 与订单详情等页一致的 table-box 容器 */
|
|
|
+.table-box {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ width: 100%;
|
|
|
+ height: auto !important;
|
|
|
+ min-height: 100%;
|
|
|
+ background-color: #ffffff;
|
|
|
+}
|
|
|
+.compare-page .content {
|
|
|
+ flex: 1;
|
|
|
+ padding: 16px 24px 24px;
|
|
|
+}
|
|
|
+.ai-link {
|
|
|
+ font-size: 14px;
|
|
|
+ color: var(--el-color-primary);
|
|
|
+ text-decoration: none;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+.ai-drawer-body {
|
|
|
+ padding: 0 4px;
|
|
|
+}
|
|
|
+.ai-analyzing {
|
|
|
+ padding: 60px 24px;
|
|
|
+ font-size: 16px;
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+.ai-section {
|
|
|
+ padding: 16px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ background: var(--el-fill-color-light);
|
|
|
+ border: 1px solid var(--el-border-color-lighter);
|
|
|
+ border-radius: 8px;
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+.ai-section-title {
|
|
|
+ padding-bottom: 8px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ border-bottom: 1px solid var(--el-border-color-lighter);
|
|
|
+}
|
|
|
+.ai-section-content {
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 1.7;
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
+ word-break: break-word;
|
|
|
+ white-space: pre-wrap;
|
|
|
+}
|
|
|
+:deep(.ai-analysis-drawer.el-drawer) {
|
|
|
+ .el-drawer__header {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ }
|
|
|
+ .el-drawer__body {
|
|
|
+ padding: 0 20px 20px;
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+}
|
|
|
+.date-pk-bar {
|
|
|
+ padding: 12px 16px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #ffffff;
|
|
|
+ text-align: center;
|
|
|
+ background: #5a5e66;
|
|
|
+ border-radius: 8px;
|
|
|
+}
|
|
|
+.content-card {
|
|
|
+ border-radius: 8px;
|
|
|
+ :deep(.el-card__body) {
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+.data-tabs {
|
|
|
+ :deep(.el-tabs__header) {
|
|
|
+ margin: 0 16px;
|
|
|
+ border-bottom: 1px solid var(--el-border-color-lighter);
|
|
|
+ }
|
|
|
+ :deep(.el-tabs__content) {
|
|
|
+ padding: 20px 16px;
|
|
|
+ }
|
|
|
+ :deep(.el-tabs__item.is-active) {
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+}
|
|
|
+.compare-cards {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 16px;
|
|
|
+ &.placeholder-tip {
|
|
|
+ padding: 40px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+}
|
|
|
+.compare-card {
|
|
|
+ padding: 16px;
|
|
|
+ background: var(--el-fill-color-light);
|
|
|
+ border-radius: 8px;
|
|
|
+ .card-title {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
+ }
|
|
|
+ .card-values {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: baseline;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ .current-value {
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ }
|
|
|
+ .compare-value {
|
|
|
+ font-size: 13px;
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .card-change {
|
|
|
+ display: inline-flex;
|
|
|
+ gap: 4px;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-size: 13px;
|
|
|
+ &.up {
|
|
|
+ color: var(--el-color-success);
|
|
|
+ }
|
|
|
+ &.down {
|
|
|
+ color: var(--el-color-danger);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .card-chart {
|
|
|
+ height: 28px;
|
|
|
+ color: var(--el-color-success);
|
|
|
+ .mini-chart {
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+ &.down {
|
|
|
+ color: var(--el-color-danger);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (width <= 1024px) {
|
|
|
+ .compare-cards {
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (width <= 600px) {
|
|
|
+ .compare-cards {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 价目表排名表格样式 */
|
|
|
+.price-list-table {
|
|
|
+ width: 100%;
|
|
|
+ font-size: 14px;
|
|
|
+ border-collapse: collapse;
|
|
|
+ th {
|
|
|
+ padding: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ text-align: left;
|
|
|
+ border-bottom: 1px solid var(--el-border-color-lighter);
|
|
|
+ }
|
|
|
+ td {
|
|
|
+ padding: 12px;
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
+ border-bottom: 1px solid var(--el-border-color-lighter);
|
|
|
+ }
|
|
|
+ .value-group {
|
|
|
+ display: flex;
|
|
|
+ flex-flow: row wrap;
|
|
|
+ gap: 8px 12px;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ .current-value {
|
|
|
+ font-weight: 500;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ }
|
|
|
+ .compare-value {
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
+ }
|
|
|
+ .change-value {
|
|
|
+ font-size: 12px;
|
|
|
+ &.up {
|
|
|
+ color: var(--el-color-success);
|
|
|
+ }
|
|
|
+ &.down {
|
|
|
+ color: var(--el-color-danger);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|