From b9c361da3b0613c22f1d167c418d700254d5d209 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 26 三月 2026 17:54:57 +0800
Subject: [PATCH] 生产统计看板(部分开发)
---
src/views/reportAnalysis/salesStatistics/index.vue | 2274 ++++++++++++++++++++++++++++++++++++++++++-----------------
1 files changed, 1,626 insertions(+), 648 deletions(-)
diff --git a/src/views/reportAnalysis/salesStatistics/index.vue b/src/views/reportAnalysis/salesStatistics/index.vue
index d1c76d4..165f30a 100644
--- a/src/views/reportAnalysis/salesStatistics/index.vue
+++ b/src/views/reportAnalysis/salesStatistics/index.vue
@@ -1,234 +1,265 @@
<template>
- <div class="sales-statistics-container">
- <div class="data-dashboard">
- <!-- 椤甸潰鏍囬 -->
- <!-- <div class="dashboard-header">
- <div class="factory-name">閿�鍞粺璁$湅鏉�</div>
- </div> -->
- <!-- 绛涢�夋潯浠� -->
- <div class="filter-area">
- <div class="filter-section">
- <span class="filter-label">鏃堕棿鑼冨洿锛�</span>
- <el-date-picker v-model="dateRange"
- type="daterange"
- range-separator="鑷�"
- start-placeholder="寮�濮嬫棩鏈�"
- end-placeholder="缁撴潫鏃ユ湡"
- value-format="YYYY-MM-DD"
- @change="handleDateChange"
- style="width: 240px;" />
+ <div ref="screenRoot"
+ class="sales-statistics-container"
+ :class="{ 'is-fullscreen': isFullscreen }">
+ <div class="bi-bg"></div>
+ <div class="bi-topbar">
+ <img class="bi-topbar-title-bg"
+ src="@/assets/BI/biaoti.png"
+ alt="閿�鍞湅鏉跨粺璁�" />
+ <div class="bi-topbar-content">
+ <div class="bi-topbar-left">
+ <button class="fullscreen-btn"
+ @click="toggleFullscreen"
+ :title="isFullscreen ? '閫�鍑哄叏灞�' : '鍏ㄥ睆鏄剧ず'">
+ <svg v-if="!isFullscreen"
+ width="1.6vh"
+ height="1.6vh"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2">
+ <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3" />
+ </svg>
+ <svg v-else
+ width="1.6vh"
+ height="1.6vh"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2">
+ <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
+ </svg>
+ </button>
+ <!-- <span class="status-sun">鈽�</span>
+ <span>26鈩�</span>
+ <span class="bi-topbar-sep">婀垮害锛�1</span> -->
</div>
- <div class="filter-section">
- <span class="filter-label">浜у搧绫诲瀷锛�</span>
- <el-select v-model="productType"
- placeholder="璇烽�夋嫨浜у搧绫诲瀷"
- @change="handleFilterChange"
- style="width: 160px;">
- <el-option label="鍏ㄩ儴"
- value="" />
- <el-option label="鐮屽潡"
- value="block" />
- <el-option label="鏉挎潗"
- value="board" />
- <el-option label="鍨嬫潗"
- value="profile" />
- </el-select>
- </div>
- <div class="filter-section">
- <span class="filter-label">閿�鍞尯鍩燂細</span>
- <el-select v-model="salesArea"
- placeholder="璇烽�夋嫨閿�鍞尯鍩�"
- @change="handleFilterChange"
- style="width: 160px;">
- <el-option label="鍏ㄩ儴"
- value="" />
- <el-option label="鍗庝笢"
- value="east" />
- <el-option label="鍗庡寳"
- value="north" />
- <el-option label="鍗庡崡"
- value="south" />
- <el-option label="瑗垮崡"
- value="southwest" />
- <el-option label="瑗垮寳"
- value="northwest" />
- </el-select>
- </div>
- <div class="filter-section">
- <span class="filter-label">缁熻缁村害锛�</span>
- <el-select v-model="statDimension"
- placeholder="璇烽�夋嫨缁熻缁村害"
- @change="handleFilterChange"
- style="width: 160px;">
- <el-option label="鏈堝害"
- value="month" />
- <el-option label="骞村害"
- value="year" />
- </el-select>
+ <div class="bi-topbar-title">閿�鍞湅鏉跨粺璁�</div>
+ <div class="bi-topbar-meta">
+ <span class="bi-topbar-time">{{ currentTime }}</span>
+ <span class="bi-topbar-sep">|</span>
+ <span class="bi-topbar-date">{{ currentDateText }}</span>
</div>
</div>
- <div class="dashboard-content">
- <!-- 鏍稿績鎸囨爣鍗$墖 -->
- <div class="row row-1">
- <div class="panel-card card-1">
- <div class="panel-title">鍚堣閿�閲�</div>
- <div class="stats-grid">
- <div class="stat-item">
- <div class="stat-value sales-volume-color">{{ totalSalesVolume }}</div>
- <div class="stat-unit">绔嬫柟绫�</div>
- <div class="stat-change">{{ salesVolumeChange }}%</div>
- </div>
- </div>
+ </div>
+ <div class="bi-dashboard-grid">
+ <!-- 宸︿笂锛氶攢閲忚秼鍔� -->
+ <div class="bi-panel bi-panel-top-left">
+ <PanelHeader :isFullscreen="true"
+ title="閿�閲忓垎鏋愯秼鍔垮浘" />
+ <div class="panel-tabs">
+ <span class="tab-item"
+ :class="{ active: blockTimeDimension === 'year' }"
+ @click="handleBlockTimeDimensionChange('year')">骞�</span>
+ <span class="tab-item"
+ :class="{ active: blockTimeDimension === 'month' }"
+ @click="handleBlockTimeDimensionChange('month')">鏈�</span>
+ </div>
+ <div class="panel-tabs2">
+ <span class="tab-item"
+ :class="{ active: blockProductType === '鐮屽潡' }"
+ @click="handleBlockProductTypeChange('鐮屽潡')">鐮屽潡</span>
+ <span class="tab-item"
+ :class="{ active: blockProductType === '鏉挎潗' }"
+ @click="handleBlockProductTypeChange('鏉挎潗')">鏉挎潗</span>
+ </div>
+ <div class="bi-panel-body">
+ <div class="chart-unit-row">
+ <span>鍗曚綅锛氱珛鏂圭背</span>
</div>
- <div class="panel-card card-2">
- <div class="panel-title">閿�鍞噾棰�</div>
- <div class="stats-grid">
- <div class="stat-item">
- <div class="stat-value sales-amount-color">{{ totalSalesAmount }}</div>
- <div class="stat-unit">涓囧厓</div>
- <div class="stat-change">{{ salesAmountChange }}%</div>
- </div>
- </div>
+ <div ref="salesVolumeChart"
+ class="echart-fill"></div>
+ </div>
+ </div>
+ <!-- 鍙充笂锛氶攢鍞噾棰� -->
+ <div class="bi-panel bi-panel-top-right">
+ <PanelHeader :isFullscreen="true"
+ title="閿�鍞噾棰濆垎鏋�" />
+ <div class="panel-tabs">
+ <span class="tab-item"
+ :class="{ active: boardTimeDimension === 'year' }"
+ @click="handleBoardTimeDimensionChange('year')">骞�</span>
+ <span class="tab-item"
+ :class="{ active: boardTimeDimension === 'month' }"
+ @click="handleBoardTimeDimensionChange('month')">鏈�</span>
+ </div>
+ <div class="panel-tabs2">
+ <span class="tab-item"
+ :class="{ active: boardProductType === '鐮屽潡' }"
+ @click="handleBoardProductTypeChange('鐮屽潡')">鐮屽潡</span>
+ <span class="tab-item"
+ :class="{ active: boardProductType === '鏉挎潗' }"
+ @click="handleBoardProductTypeChange('鏉挎潗')">鏉挎潗</span>
+ </div>
+ <div class="bi-panel-body">
+ <div class="chart-unit-row">
+ <span>鍗曚綅锛氬厓</span>
</div>
- <div class="panel-card card-3">
- <div class="panel-title">鏂板瀹㈡埛</div>
- <div class="stats-grid">
- <div class="stat-item">
- <div class="stat-value new-customer-color">{{ newCustomerCount }}</div>
- <div class="stat-unit">涓�</div>
- <div class="stat-change">{{ customerCountChange }}%</div>
- </div>
- </div>
+ <div ref="salesAmountChart"
+ class="echart-fill"></div>
+ </div>
+ </div>
+ <!-- 涓棿涓績鐜� -->
+ <div class="center-ring">
+ <div class="center-ring-box">
+ <div class="center-metric m1">
+ <div class="center-metric-label">鎬婚攢鍞噾棰�</div>
+ <div class="center-metric-value">{{ totalSalesAmount.toFixed(0) }}</div>
+ <div class="center-metric-unit">涓囧厓</div>
</div>
- <div class="panel-card card-4">
- <div class="panel-title">鍚堣瀹㈡埛</div>
- <div class="stats-grid">
- <div class="stat-item">
- <div class="stat-value total-customer-color">{{ totalCustomerCount }}</div>
- <div class="stat-unit">涓�</div>
- <div class="stat-change">{{ totalCustomerChange }}%</div>
- </div>
- </div>
+ <div class="center-metric m2">
+ <div class="center-metric-label">鎴愪氦鎬昏鍗�</div>
+ <div class="center-metric-value">{{ completedOrders }}</div>
+ <div class="center-metric-unit">鍗�</div>
+ </div>
+ <div class="center-metric m3">
+ <div class="center-metric-label">绱瀹㈡埛</div>
+ <div class="center-metric-value">{{ centerNewCustomerCount }}</div>
+ <div class="center-metric-unit">瀹�</div>
+ </div>
+ <div class="center-metric m4">
+ <div class="center-metric-label">鎬婚攢鍞尯</div>
+ <div class="center-metric-value">{{ totalSalesAreaCount }}</div>
+ <div class="center-metric-unit">鍖�</div>
</div>
</div>
- <!-- 閿�閲忓拰閿�鍞噾棰濊秼鍔� -->
- <div class="row row-2">
- <div class="panel-card card-5">
- <div class="panel-title">閿�閲忚秼鍔�</div>
- <div class="chart-container">
- <div ref="salesVolumeChart"
- style="width: 100%; height: 100%;"></div>
- </div>
+ </div>
+ <!-- 宸︿笅锛氫骇鍝佺被鍨嬮攢閲� -->
+ <div class="bi-panel bi-panel-bottom-left">
+ <PanelHeader :isFullscreen="true"
+ title="閿�閲忔暟鎹�-鎺掑悕鍒嗘瀽" />
+ <div class="panel-tabs">
+ <span class="tab-item"
+ :class="{ active: blockTimeDimension === 'year' }"
+ @click="handleBlockTimeDimensionChange('year')">骞�</span>
+ <span class="tab-item"
+ :class="{ active: blockTimeDimension === 'month' }"
+ @click="handleBlockTimeDimensionChange('month')">鏈�</span>
+ </div>
+ <div class="panel-tabs2">
+ <span class="tab-item"
+ :class="{ active: blockProductType === '鐮屽潡' }"
+ @click="handleBlockProductTypeChange('鐮屽潡')">鐮屽潡</span>
+ <span class="tab-item"
+ :class="{ active: blockProductType === '鏉挎潗' }"
+ @click="handleBlockProductTypeChange('鏉挎潗')">鏉挎潗</span>
+ </div>
+ <div class="bi-panel-body">
+ <div class="chart-filter-tabs">
+ <span v-for="area in salesAreas"
+ :key="area"
+ class="cf-tab"
+ :class="{ active: blockSelectedArea === area }"
+ @click="handleBlockAreaChange(area)">{{ area }}</span>
</div>
- <div class="panel-card card-6">
- <div class="panel-title">閿�鍞噾棰濊秼鍔�</div>
- <div class="chart-container">
- <div ref="salesAmountChart"
- style="width: 100%; height: 100%;"></div>
- </div>
+ <div class="scroll-table-container">
+ <table class="scroll-table">
+ <thead>
+ <tr>
+ <th>鎺掑悕</th>
+ <th>浜у搧绫诲瀷</th>
+ <th>骞存湀</th>
+ <th>閿�鍞尯</th>
+ <th>閿�閲忥紙m鲁锛�</th>
+ </tr>
+ </thead>
+ <div class="scroll-table-content">
+ <tbody ref="blockTableBody">
+ <tr :class="item.sort % 2 === 0 ? 'evenTableTr' : 'oddTableTr'"
+ v-for="(item, index) in blockSalesData"
+ :key="item.period + item.area + index">
+ <td>{{ item.sort }}</td>
+ <td>{{ item.productType }}</td>
+ <td>{{ item.period }}</td>
+ <td>{{ item.area }}</td>
+ <td>{{ item.sales }}</td>
+ </tr>
+ </tbody>
+ </div>
+ </table>
+ </div>
+ <div class="panel-summary-row">
+ <div class="summary-label">鍚堣</div>
+ <div class="summary-value">127384 m鲁</div>
</div>
</div>
- <!-- 绱鏁版嵁瓒嬪娍 -->
- <!-- <div class="row row-3">
- <div class="panel-card card-10">
- <div class="panel-title">绱閿�閲忚秼鍔�</div>
- <div class="chart-container">
- <div ref="cumulativeSalesVolumeChart"
- style="width: 100%; height: 100%;"></div>
- </div>
+ </div>
+ <!-- 涓笅锛氭柊澧炲鎴峰垎鏋愶紙鍒嗕骇鍝佺被鍨嬭秼鍔匡級 -->
+ <div class="bi-panel bi-panel-bottom-center">
+ <PanelHeader :isFullscreen="true"
+ title="鏂板瀹㈡埛瓒嬪娍鍒嗘瀽" />
+ <div class="panel-tabs">
+ <span class="tab-item"
+ :class="{ active: customerTimeDimension === 'year' }"
+ @click="handleCustomerTimeDimensionChange('year')">骞�</span>
+ <span class="tab-item"
+ :class="{ active: customerTimeDimension === 'month' }"
+ @click="handleCustomerTimeDimensionChange('month')">鏈�</span>
+ </div>
+ <div class="bi-panel-body">
+ <div class="chart-unit-row chart-unit-single">
+ <span>鍗曚綅锛氬</span>
</div>
- <div class="panel-card card-11">
- <div class="panel-title">绱閿�鍞噾棰濊秼鍔�</div>
- <div class="chart-container">
- <div ref="cumulativeSalesAmountChart"
- style="width: 100%; height: 100%;"></div>
- </div>
+ <div ref="productTypeTrendChart"
+ class="echart-fill"></div>
+ </div>
+ </div>
+ <!-- 鍙充笅锛氶攢鍞尯鍩熼攢閲� -->
+ <div class="bi-panel bi-panel-bottom-right">
+ <PanelHeader :isFullscreen="true"
+ title="閿�鍞鏁版嵁-鎺掑悕鍒嗘瀽" />
+ <div class="panel-tabs">
+ <span class="tab-item"
+ :class="{ active: boardTimeDimension === 'year' }"
+ @click="handleBoardTimeDimensionChange('year')">骞�</span>
+ <span class="tab-item"
+ :class="{ active: boardTimeDimension === 'month' }"
+ @click="handleBoardTimeDimensionChange('month')">鏈�</span>
+ </div>
+ <div class="panel-tabs2">
+ <span class="tab-item"
+ :class="{ active: boardProductType === '鐮屽潡' }"
+ @click="handleBoardProductTypeChange('鐮屽潡')">鐮屽潡</span>
+ <span class="tab-item"
+ :class="{ active: boardProductType === '鏉挎潗' }"
+ @click="handleBoardProductTypeChange('鏉挎潗')">鏉挎潗</span>
+ </div>
+ <div class="bi-panel-body">
+ <div class="chart-filter-tabs">
+ <span v-for="area in salesAreas"
+ :key="area"
+ class="cf-tab"
+ :class="{ active: boardSelectedArea === area }"
+ @click="handleBoardAreaChange(area)">{{ area }}</span>
</div>
- </div> -->
- <!-- 鍥捐〃鍖哄煙鍜岃〃鏍� -->
- <div class="row row-4">
- <!-- 宸﹁竟锛氳缁嗘暟鎹〃鏍� -->
- <div class="panel-card card-9"
- style="flex: 2;">
- <div class="panel-title">閿�鍞粺璁¤缁嗘暟鎹�</div>
- <div class="table-container">
- <el-table :data="tableData"
- style="width: 100%">
- <el-table-column prop="productType"
- label="浜у搧绫诲瀷"
- width="120"
- align="center">
- <template #default="scope">
- <el-tag :type="getProductTypeType(scope.row.productType)">
- {{ scope.row.productType }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="salesArea"
- label="閿�鍞尯鍩�"
- width="120"
- align="center">
- <template #default="scope">
- <el-tag :type="getSalesAreaType(scope.row.salesArea)">
- {{ scope.row.salesArea }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="period"
- label="缁熻鍛ㄦ湡"
- width="120" />
- <el-table-column prop="salesVolume"
- label="閿�閲�(绔嬫柟绫�)"
- align="right">
- <template #default="scope">
- <span class="data-value">{{ scope.row.salesVolume }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="salesAmount"
- label="閿�鍞噾棰�(涓囧厓)"
- align="right">
- <template #default="scope">
- <span class="data-value">{{ scope.row.salesAmount }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="newCustomers"
- label="鏂板瀹㈡埛(涓�)"
- width="150"
- align="right">
- <template #default="scope">
- <span class="data-value">{{ scope.row.newCustomers }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="totalCustomers"
- label="鍚堣瀹㈡埛(涓�)"
- width="150"
- align="right">
- <template #default="scope">
- <span class="data-value">{{ scope.row.totalCustomers }}</span>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </div>
- <!-- 鍙宠竟锛氫骇鍝佺被鍨嬪垎甯冨拰閿�鍞尯鍩熷垎甯� -->
- <div class="chart-column"
- style="flex: 1; display: flex; flex-direction: column; gap: 20px;">
- <div class="panel-card card-7"
- style="flex: 1;">
- <div class="panel-title">浜у搧绫诲瀷鍒嗗竷</div>
- <div class="chart-container">
- <div ref="productTypeChart"
- style="width: 100%; height: 100%;"></div>
+ <div class="scroll-table-container">
+ <table class="scroll-table">
+ <thead>
+ <tr>
+ <th>鎺掑悕</th>
+ <th>骞存湀</th>
+ <th>閿�鍞尯</th>
+ <th>閿�鍞锛堜竾鍏冿級</th>
+ </tr>
+ </thead>
+ <div class="scroll-table-content">
+ <tbody ref="boardTableBody">
+ <tr :class="item.sort % 2 === 0 ? 'evenTableTr' : 'oddTableTr'"
+ v-for="(item, index) in boardSalesData"
+ :key="item.period + item.area + index">
+ <td>{{ item.sort }}</td>
+ <td>{{ item.period }}</td>
+ <td>{{ item.area }}</td>
+ <td>{{ item.sales }}</td>
+ </tr>
+ </tbody>
</div>
- </div>
- <div class="panel-card card-8"
- style="flex: 1;">
- <div class="panel-title">閿�鍞尯鍩熷垎甯�</div>
- <div class="chart-container">
- <div ref="salesAreaChart"
- style="width: 100%; height: 100%;"></div>
- </div>
- </div>
+ </table>
+ </div>
+ <div class="panel-summary-row">
+ <div class="summary-label">鍚堣</div>
+ <div class="summary-value2">127384 涓囧厓</div>
</div>
</div>
</div>
@@ -248,8 +279,50 @@
import { useRouter } from "vue-router";
import * as echarts from "echarts";
import dayjs from "dayjs";
+ import PanelHeader from "@/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue";
+ import { findAllQualifiedStockOutRecordTypeOptions } from "../../../api/basicData/enum";
const router = useRouter();
+ const screenRoot = ref(null);
+ const isFullscreen = ref(false);
+
+ // 椤堕儴鏍忔椂闂达紙鐢ㄤ簬鍖归厤BI澶у睆鏁堟灉鍥撅級
+ const now = ref(dayjs());
+ const currentTime = computed(() => now.value.format("HH:mm:ss"));
+ const currentDateText = computed(() => {
+ const weekMap = {
+ 0: "鏄熸湡鏃�",
+ 1: "鏄熸湡涓�",
+ 2: "鏄熸湡浜�",
+ 3: "鏄熸湡涓�",
+ 4: "鏄熸湡鍥�",
+ 5: "鏄熸湡浜�",
+ 6: "鏄熸湡鍏�",
+ };
+ return `${now.value.format("YYYY-MM-DD")} ${weekMap[now.value.day()] || ""}`;
+ });
+ let timeTicker = null;
+
+ const handleFullscreenChange = () => {
+ isFullscreen.value = !!document.fullscreenElement;
+ nextTick(() => {
+ handleResize();
+ });
+ };
+
+ const toggleFullscreen = async () => {
+ const rootEl = screenRoot.value;
+ if (!rootEl) return;
+ try {
+ if (!document.fullscreenElement) {
+ await rootEl.requestFullscreen();
+ } else {
+ await document.exitFullscreen();
+ }
+ } catch (error) {
+ console.error("鍏ㄥ睆鍒囨崲澶辫触:", error);
+ }
+ };
// 绛涢�夋潯浠�
const dateRange = ref([]);
@@ -262,6 +335,54 @@
const salesAmountChart = ref(null);
const productTypeChart = ref(null);
const salesAreaChart = ref(null);
+ const productTypeTrendChart = ref(null);
+ const blockTableBody = ref(null);
+ const boardTableBody = ref(null);
+
+ // 閫夋嫨鍣ㄦ暟鎹�
+ const blockTimeDimension = ref("year");
+ const blockSelectedArea = ref("鍏ㄩ儴");
+ const blockProductType = ref("鐮屽潡");
+ const boardTimeDimension = ref("year");
+ const boardSelectedArea = ref("鍏ㄩ儴");
+ const boardProductType = ref("鏉挎潗");
+ const customerTimeDimension = ref("year");
+
+ const salesAreas = [
+ "鍏ㄩ儴",
+ "A閿�鍞尯",
+ "B閿�鍞尯",
+ "C閿�鍞尯",
+ "D閿�鍞尯",
+ "E閿�鍞尯",
+ ];
+
+ // 琛ㄦ牸鏁版嵁
+ const blockSalesData = ref([
+ { period: "2024-01", area: "***閿�鍞尯", sales: 1250, sort: 1 },
+ { period: "2024-02", area: "xxx閿�鍞尯", sales: 1180, sort: 2 },
+ { period: "2024-03", area: "xxx閿�鍞尯", sales: 1050, sort: 3 },
+ { period: "2024-04", area: "xxx閿�鍞尯", sales: 980, sort: 4 },
+ { period: "2024-05", area: "xxx閿�鍞尯", sales: 920, sort: 5 },
+ { period: "2024-06", area: "***閿�鍞尯", sales: 880, sort: 6 },
+ { period: "2024-07", area: "xxx閿�鍞尯", sales: 850, sort: 7 },
+ { period: "2024-08", area: "***閿�鍞尯", sales: 820, sort: 8 },
+ { period: "2024-09", area: "xxx閿�鍞尯", sales: 790, sort: 9 },
+ { period: "2024-10", area: "***閿�鍞尯", sales: 750, sort: 10 },
+ ]);
+
+ const boardSalesData = ref([
+ { period: "2024-01", area: "***閿�鍞尯", sales: 980, sort: 1 },
+ { period: "2024-02", area: "xxx閿�鍞尯", sales: 920, sort: 2 },
+ { period: "2024-03", area: "***閿�鍞尯", sales: 880, sort: 3 },
+ { period: "2024-04", area: "xxx閿�鍞尯", sales: 850, sort: 4 },
+ { period: "2024-05", area: "xxx閿�鍞尯", sales: 820, sort: 5 },
+ { period: "2024-06", area: "***閿�鍞尯", sales: 790, sort: 6 },
+ { period: "2024-07", area: "xxx閿�鍞尯", sales: 750, sort: 7 },
+ { period: "2024-08", area: "xxx閿�鍞尯", sales: 720, sort: 8 },
+ { period: "2024-09", area: "***閿�鍞尯", sales: 690, sort: 9 },
+ { period: "2024-10", area: "xxx閿�鍞尯", sales: 650, sort: 10 },
+ ]);
const cumulativeSalesVolumeChart = ref(null);
const cumulativeSalesAmountChart = ref(null);
@@ -270,6 +391,7 @@
let salesAmountChartInstance = null;
let productTypeChartInstance = null;
let salesAreaChartInstance = null;
+ let productTypeTrendChartInstance = null;
let cumulativeSalesVolumeChartInstance = null;
let cumulativeSalesAmountChartInstance = null;
@@ -527,11 +649,7 @@
return filteredData.value.reduce((sum, item) => sum + item.salesVolume, 0);
});
- const totalSalesAmount = computed(() => {
- return filteredData.value
- .reduce((sum, item) => sum + item.salesAmount, 0)
- .toFixed(2);
- });
+ const totalSalesAmount = ref(1299);
const newCustomerCount = computed(() => {
return filteredData.value.reduce((sum, item) => sum + item.newCustomers, 0);
@@ -548,6 +666,12 @@
});
return Object.values(customerMap).reduce((sum, count) => sum + count, 0);
});
+
+ // 涓棿涓績鐜寚鏍囷紙鐢ㄤ簬澶у睆灞曠ず锛屼娇鐢ㄧ幇鏈夌粺璁℃暟鎹仛鏄犲皠锛�
+ const centerNewCustomerCount = computed(() => 112);
+ const completedOrders = computed(() => 1829);
+ const salesOrderCount = computed(() => 34);
+ const totalSalesAreaCount = computed(() => 12);
// 鍙樺寲鐜囪绠楋紙妯℃嫙锛�
const salesVolumeChange = ref("+5.2");
@@ -574,272 +698,522 @@
// 閿�閲忚秼鍔垮浘琛ㄩ厤缃�
const salesVolumeChartOption = computed(() => {
- // 鎸夊懆鏈熷垎缁�
- const periodMap = {};
- filteredData.value.forEach(item => {
- if (!periodMap[item.period]) {
- periodMap[item.period] = 0;
+ // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
+ const salesAreas = [
+ "鍏ㄩ儴",
+ "A閿�鍞尯",
+ "B閿�鍞尯",
+ "C閿�鍞尯",
+ "D閿�鍞尯",
+ "E閿�鍞尯",
+ ];
+ const colors = [
+ "#00A4ED",
+ "#34D8F7",
+ "#4A8BFF",
+ "#8A6BFF",
+ "#C8C447",
+ "#FF6B6B",
+ ];
+ const year = 2024;
+ const periodType = blockTimeDimension.value;
+
+ // 鐢熸垚鏃堕棿娈�
+ let periods = [];
+ if (periodType === "year") {
+ // 骞村害鏁版嵁锛�12涓湀
+ for (let month = 1; month <= 12; month++) {
+ periods.push(`${year}-${month.toString().padStart(2, "0")}`);
}
- periodMap[item.period] += item.salesVolume;
+ } else {
+ // 鏈堝害鏁版嵁锛�30澶�
+ const month = 1;
+ for (let day = 1; day <= 30; day++) {
+ periods.push(
+ `${year}-${month.toString().padStart(2, "0")}-${day
+ .toString()
+ .padStart(2, "0")}`
+ );
+ }
+ }
+
+ // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
+ const series = salesAreas.map((area, index) => {
+ const data = periods.map(() => {
+ return periodType === "year"
+ ? Math.floor(Math.random() * 500) + 800
+ : Math.floor(Math.random() * 50) + 20;
+ });
+
+ return {
+ name: area,
+ data: data,
+ type: "line",
+ smooth: false,
+ // symbolSize: getResponsiveValue(8),
+ lineStyle: { width: getResponsiveValue(1), color: colors[index] },
+ itemStyle: { color: colors[index] },
+ areaStyle: {
+ opacity: 0.4,
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: colors[index] + "80" },
+ { offset: 1, color: colors[index] + "00" },
+ ]),
+ },
+ };
});
- const periods = Object.keys(periodMap).sort();
- const values = periods.map(period => periodMap[period]);
-
return {
+ backgroundColor: "transparent",
tooltip: {
trigger: "axis",
- formatter: "{b}: {c} 绔嬫柟绫�",
+ backgroundColor: "rgba(0,0,0,0.55)",
+ borderColor: "rgba(64,158,255,0.25)",
+ borderWidth: getResponsiveValue(1),
+ textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
+ formatter: function (params) {
+ let result = params[0].name + "<br/>";
+ params.forEach(param => {
+ result += `${param.marker}${param.seriesName}: ${param.value} 绔嬫柟绫�<br/>`;
+ });
+ return result;
+ },
+ },
+ legend: {
+ data: salesAreas,
+ top: "10%",
+ right: "1%",
+ textStyle: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(9),
+ },
+ itemWidth: getResponsiveValue(10),
+ itemHeight: getResponsiveValue(10),
+ },
+ grid: {
+ left: "1%",
+ right: "1%",
+ bottom: "1%",
+ top: "28%",
+ containLabel: true,
},
xAxis: {
type: "category",
data: periods,
+ axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
+ axisTick: { show: false },
+ axisLabel: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(11),
+ margin: getResponsiveValue(10),
+ },
+ splitLine: { show: false },
},
yAxis: {
type: "value",
- name: "閿�閲忥紙绔嬫柟绫筹級",
- },
- series: [
- {
- data: values,
- type: "line",
- smooth: true,
- lineStyle: {
- width: 3,
- },
- itemStyle: {
- color: "#409EFF",
- },
+ name: "",
+ axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
+ axisLabel: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(11),
+ margin: getResponsiveValue(8),
},
- ],
+ splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
+ },
+ series: series,
};
});
// 閿�鍞噾棰濊秼鍔垮浘琛ㄩ厤缃�
const salesAmountChartOption = computed(() => {
- // 鎸夊懆鏈熷垎缁�
- const periodMap = {};
- filteredData.value.forEach(item => {
- if (!periodMap[item.period]) {
- periodMap[item.period] = 0;
+ // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
+ const salesAreas = [
+ "鍏ㄩ儴",
+ "A閿�鍞尯",
+ "B閿�鍞尯",
+ "C閿�鍞尯",
+ "D閿�鍞尯",
+ "E閿�鍞尯",
+ ];
+ const colors = [
+ "#00A4ED",
+ "#34D8F7",
+ "#4A8BFF",
+ "#8A6BFF",
+ "#C8C447",
+ "#FF6B6B",
+ ];
+ const year = 2024;
+ const periodType = boardTimeDimension.value;
+
+ // 鐢熸垚鏃堕棿娈�
+ let periods = [];
+ if (periodType === "year") {
+ // 骞村害鏁版嵁锛�12涓湀
+ for (let month = 1; month <= 12; month++) {
+ periods.push(`${year}-${month.toString().padStart(2, "0")}`);
}
- periodMap[item.period] += item.salesAmount;
+ } else {
+ // 鏈堝害鏁版嵁锛�30澶�
+ const month = 1;
+ for (let day = 1; day <= 30; day++) {
+ periods.push(
+ `${year}-${month.toString().padStart(2, "0")}-${day
+ .toString()
+ .padStart(2, "0")}`
+ );
+ }
+ }
+
+ // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
+ const series = salesAreas.map((area, index) => {
+ const data = periods.map(() => {
+ return periodType === "year"
+ ? Math.floor(Math.random() * 50000) + 80000
+ : Math.floor(Math.random() * 5000) + 2000;
+ });
+
+ return {
+ name: area,
+ data: data,
+ type: "bar",
+ smooth: true,
+ lineStyle: { width: getResponsiveValue(3), color: colors[index] },
+ itemStyle: { color: colors[index] },
+ areaStyle: {
+ opacity: 0.2,
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: colors[index] + "80" },
+ { offset: 1, color: colors[index] + "00" },
+ ]),
+ },
+ };
});
- const periods = Object.keys(periodMap).sort();
- const values = periods.map(period => periodMap[period]);
-
return {
+ backgroundColor: "transparent",
tooltip: {
trigger: "axis",
- formatter: "{b}: {c} 涓囧厓",
+ backgroundColor: "rgba(0,0,0,0.55)",
+ borderColor: "rgba(64,158,255,0.25)",
+ borderWidth: getResponsiveValue(1),
+ textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
+ formatter: function (params) {
+ let result = params[0].name + "<br/>";
+ params.forEach(param => {
+ result += `${param.marker}${param.seriesName}: ${param.value} 鍏�<br/>`;
+ });
+ return result;
+ },
+ },
+ legend: {
+ data: salesAreas,
+ top: "10%",
+ right: "1%",
+ textStyle: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(9),
+ },
+ itemWidth: getResponsiveValue(10),
+ itemHeight: getResponsiveValue(10),
+ },
+ grid: {
+ left: "1%",
+ right: "1%",
+ bottom: "1%",
+ top: "28%",
+ containLabel: true,
},
xAxis: {
type: "category",
data: periods,
+ axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
+ axisTick: { show: false },
+ axisLabel: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(11),
+ margin: getResponsiveValue(10),
+ },
+ splitLine: { show: false },
},
yAxis: {
type: "value",
- name: "閿�鍞噾棰濓紙涓囧厓锛�",
- },
- series: [
- {
- data: values,
- type: "bar",
- itemStyle: {
- color: "#67C23A",
- },
+ name: "",
+ axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
+ axisLabel: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(11),
+ margin: getResponsiveValue(8),
},
- ],
+ splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
+ },
+ series: series,
};
});
- // 浜у搧绫诲瀷鍒嗗竷鍥捐〃閰嶇疆
+ // 浜у搧绫诲瀷閿�閲忓浘琛ㄩ厤缃紙妯悜鏌辩姸锛�
const productTypeChartOption = computed(() => {
- // 鎸変骇鍝佺被鍨嬪垎缁�
- const typeMap = {};
- filteredData.value.forEach(item => {
- if (!typeMap[item.productType]) {
- typeMap[item.productType] = 0;
- }
- typeMap[item.productType] += item.salesVolume;
- });
-
- const types = Object.keys(typeMap);
- const values = types.map(type => typeMap[type]);
+ const types = ["瀹㈡埛BB", "瀹㈡埛AA", "瀹㈡埛CC", "瀹㈡埛DD", "瀹㈡埛DD", "瀹㈡埛DD"];
+ const values = [130, 120, 102, 90, 90, 70];
+ const barColors = [
+ "#34D8F7",
+ "#4A8BFF",
+ "#8A6BFF",
+ "#C8C447",
+ "#C8C447",
+ "#C8C447",
+ ];
return {
- tooltip: {
- trigger: "item",
- formatter: "{b}: {c} 绔嬫柟绫� ({d}%)",
- },
- series: [
- {
- type: "pie",
- radius: "60%",
- data: types.map((type, index) => ({
- name: type,
- value: values[index],
- })),
- emphasis: {
- itemStyle: {
- shadowBlur: 10,
- shadowOffsetX: 0,
- shadowColor: "rgba(0, 0, 0, 0.5)",
- },
- },
- },
- ],
- };
- });
-
- // 閿�鍞尯鍩熷垎甯冨浘琛ㄩ厤缃�
- const salesAreaChartOption = computed(() => {
- // 鎸夐攢鍞尯鍩熷垎缁�
- const areaMap = {};
- filteredData.value.forEach(item => {
- if (!areaMap[item.salesArea]) {
- areaMap[item.salesArea] = 0;
- }
- areaMap[item.salesArea] += item.salesVolume;
- });
-
- const areas = Object.keys(areaMap);
- const values = areas.map(area => areaMap[area]);
-
- return {
- tooltip: {
- trigger: "item",
- formatter: "{b}: {c} 绔嬫柟绫� ({d}%)",
- },
- series: [
- {
- type: "pie",
- radius: "60%",
- data: areas.map((area, index) => ({
- name: area,
- value: values[index],
- })),
- emphasis: {
- itemStyle: {
- shadowBlur: 10,
- shadowOffsetX: 0,
- shadowColor: "rgba(0, 0, 0, 0.5)",
- },
- },
- },
- ],
- };
- });
-
- // 绱閿�閲忚秼鍔垮浘琛ㄩ厤缃�
- const cumulativeSalesVolumeChartOption = computed(() => {
- // 鎸夊懆鏈熷垎缁�
- const periodMap = {};
- let cumulativeValue = 0;
-
- // 鎸夊懆鏈熸帓搴�
- const sortedData = [...filteredData.value].sort((a, b) =>
- a.period.localeCompare(b.period)
- );
-
- sortedData.forEach(item => {
- cumulativeValue += item.salesVolume;
- periodMap[item.period] = cumulativeValue;
- });
-
- const periods = Object.keys(periodMap).sort();
- const values = periods.map(period => periodMap[period]);
-
- return {
+ backgroundColor: "transparent",
tooltip: {
trigger: "axis",
+ axisPointer: { type: "shadow" },
+ backgroundColor: "rgba(0,0,0,0.55)",
+ borderColor: "rgba(64,158,255,0.25)",
+ borderWidth: getResponsiveValue(1),
+ textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
formatter: "{b}: {c} 绔嬫柟绫�",
},
+ grid: {
+ left: "14%",
+ right: "6%",
+ top: "16%",
+ bottom: "8%",
+ containLabel: true,
+ },
xAxis: {
- type: "category",
- data: periods,
+ type: "value",
+ axisLine: { show: false },
+ axisLabel: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
+ splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
},
yAxis: {
- type: "value",
- name: "绱閿�閲忥紙绔嬫柟绫筹級",
+ type: "category",
+ data: types,
+ axisTick: { show: false },
+ axisLine: { show: false },
+ axisLabel: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(11),
+ margin: getResponsiveValue(8),
+ },
},
series: [
{
+ name: "閿�閲忥紙绔嬫柟绫筹級",
+ type: "bar",
+ barWidth: getResponsiveValue(14),
data: values,
- type: "line",
- smooth: true,
- areaStyle: {
- opacity: 0.3,
- },
itemStyle: {
- color: "#E6A23C",
+ color: params => barColors[params.dataIndex] || "#00A4ED",
+ borderRadius: [
+ getResponsiveValue(6),
+ getResponsiveValue(6),
+ getResponsiveValue(6),
+ getResponsiveValue(6),
+ ],
},
- lineStyle: {
- width: 3,
+ label: {
+ show: false,
},
},
],
};
});
- // 绱閿�鍞噾棰濊秼鍔垮浘琛ㄩ厤缃�
- const cumulativeSalesAmountChartOption = computed(() => {
- // 鎸夊懆鏈熷垎缁�
- const periodMap = {};
- let cumulativeValue = 0;
-
- // 鎸夊懆鏈熸帓搴�
- const sortedData = [...filteredData.value].sort((a, b) =>
- a.period.localeCompare(b.period)
- );
-
- sortedData.forEach(item => {
- cumulativeValue += item.salesAmount;
- periodMap[item.period] = cumulativeValue;
- });
-
- const periods = Object.keys(periodMap).sort();
- const values = periods.map(period => periodMap[period]);
+ // 閿�鍞尯鍩熼攢閲忓浘琛ㄩ厤缃紙妯悜鏌辩姸锛�
+ const salesAreaChartOption = computed(() => {
+ const areas = ["瀹㈡埛BB", "瀹㈡埛AA", "瀹㈡埛CC", "瀹㈡埛DD", "瀹㈡埛DD", "瀹㈡埛DD"];
+ const values = [130, 120, 102, 90, 90, 70];
+ const barColors = [
+ "#34D8F7",
+ "#4A8BFF",
+ "#8A6BFF",
+ "#C8C447",
+ "#C8C447",
+ "#C8C447",
+ ];
return {
+ backgroundColor: "transparent",
tooltip: {
trigger: "axis",
- formatter: "{b}: {c} 涓囧厓",
+ axisPointer: { type: "shadow" },
+ backgroundColor: "rgba(0,0,0,0.55)",
+ borderColor: "rgba(64,158,255,0.25)",
+ borderWidth: getResponsiveValue(1),
+ textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
+ formatter: "{b}: {c} 绔嬫柟绫�",
+ },
+ grid: {
+ left: "14%",
+ right: "6%",
+ top: "16%",
+ bottom: "8%",
+ containLabel: true,
},
xAxis: {
- type: "category",
- data: periods,
+ type: "value",
+ axisLine: { show: false },
+ axisLabel: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
+ splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
},
yAxis: {
- type: "value",
- name: "绱閿�鍞噾棰濓紙涓囧厓锛�",
+ type: "category",
+ data: areas,
+ axisTick: { show: false },
+ axisLine: { show: false },
+ axisLabel: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(11),
+ margin: getResponsiveValue(8),
+ },
},
series: [
{
- data: values,
+ name: "閿�閲忥紙绔嬫柟绫筹級",
type: "bar",
+ barWidth: getResponsiveValue(14),
+ data: values,
itemStyle: {
- color: "#F56C6C",
+ color: params => barColors[params.dataIndex] || "#00A4ED",
+ borderRadius: [
+ getResponsiveValue(6),
+ getResponsiveValue(6),
+ getResponsiveValue(6),
+ getResponsiveValue(6),
+ ],
},
},
],
};
});
- // 鏂规硶
- const goBack = () => {
- router.back();
- };
+ // 鏂板瀹㈡埛瓒嬪娍鍥捐〃閰嶇疆锛堟寜閿�鍞尯鍜屽勾鏈堢淮搴︼級
+ const productTypeTrendChartOption = computed(() => {
+ // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
+ const salesAreas = [
+ "鍏ㄩ儴",
+ "A閿�鍞尯",
+ "B閿�鍞尯",
+ "C閿�鍞尯",
+ "D閿�鍞尯",
+ "E閿�鍞尯",
+ ];
+ const colors = [
+ "#00A4ED",
+ "#34D8F7",
+ "#4A8BFF",
+ "#8A6BFF",
+ "#C8C447",
+ "#FF6B6B",
+ ];
+ const year = 2024;
+ const periodType = customerTimeDimension.value;
- const handleDateChange = () => {
- // 澶勭悊鏃ユ湡鍙樺寲
- updateCharts();
- };
+ // 鐢熸垚鏃堕棿娈�
+ let periods = [];
+ if (periodType === "year") {
+ // 骞村害鏁版嵁锛�12涓湀
+ for (let month = 1; month <= 12; month++) {
+ periods.push(`${year}-${month.toString().padStart(2, "0")}`);
+ }
+ } else {
+ // 鏈堝害鏁版嵁锛�30澶�
+ const month = 1;
+ for (let day = 1; day <= 30; day++) {
+ periods.push(
+ `${year}-${month.toString().padStart(2, "0")}-${day
+ .toString()
+ .padStart(2, "0")}`
+ );
+ }
+ }
- const handleFilterChange = () => {
- // 澶勭悊绛涢�夋潯浠跺彉鍖�
- updateCharts();
+ // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
+ const series = salesAreas.map((area, index) => {
+ const data = periods.map(() => {
+ return periodType === "year"
+ ? Math.floor(Math.random() * 10) + 2
+ : Math.floor(Math.random() * 3) + 1;
+ });
+
+ return {
+ name: area,
+ data: data,
+ type: "line",
+ smooth: false,
+ lineStyle: { width: getResponsiveValue(1), color: colors[index] },
+ itemStyle: { color: colors[index] },
+ };
+ });
+
+ return {
+ backgroundColor: "transparent",
+ tooltip: {
+ trigger: "axis",
+ backgroundColor: "rgba(0,0,0,0.55)",
+ borderColor: "rgba(64,158,255,0.25)",
+ borderWidth: getResponsiveValue(1),
+ textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
+ formatter: function (params) {
+ let result = params[0].name + "<br/>";
+ params.forEach(param => {
+ result += `${param.marker}${param.seriesName}: ${param.value} 瀹�<br/>`;
+ });
+ return result;
+ },
+ },
+ legend: {
+ data: salesAreas,
+ top: "10%",
+ right: "1%",
+ textStyle: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(9),
+ },
+ itemWidth: getResponsiveValue(10),
+ itemHeight: getResponsiveValue(10),
+ },
+ grid: {
+ left: "1%",
+ right: "1%",
+ bottom: "1%",
+ top: "28%",
+ containLabel: true,
+ },
+ xAxis: {
+ type: "category",
+ data: periods,
+ axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
+ axisTick: { show: false },
+ axisLabel: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(11),
+ margin: getResponsiveValue(10),
+ },
+ splitLine: { show: false },
+ },
+ yAxis: {
+ type: "value",
+ name: "",
+ axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
+ axisLabel: {
+ color: "#B8C8E0",
+ fontSize: getResponsiveValue(11),
+ margin: getResponsiveValue(8),
+ },
+ splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
+ },
+ series: series,
+ };
+ });
+
+ const baseWidth = ref(1650);
+ // 璁$畻鍝嶅簲寮忓��
+ const getResponsiveValue = baseValue => {
+ return Math.round((baseValue * window.innerWidth) / baseWidth.value);
};
// 鍒濆鍖栧浘琛�
@@ -864,6 +1238,11 @@
salesAreaChartInstance = echarts.init(salesAreaChart.value);
}
+ // 鍒濆鍖栨柊澧炲鎴疯秼鍔垮浘琛�
+ if (productTypeTrendChart.value && !productTypeTrendChartInstance) {
+ productTypeTrendChartInstance = echarts.init(productTypeTrendChart.value);
+ }
+
// 鍒濆鍖栫疮璁¢攢閲忚秼鍔垮浘琛�
if (cumulativeSalesVolumeChart.value && !cumulativeSalesVolumeChartInstance) {
cumulativeSalesVolumeChartInstance = echarts.init(
@@ -880,12 +1259,13 @@
updateCharts();
};
-
// 鏇存柊鍥捐〃
const updateCharts = () => {
// 鏇存柊閿�閲忚秼鍔垮浘琛�
if (salesVolumeChartInstance) {
- salesVolumeChartInstance.setOption(salesVolumeChartOption.value);
+ salesVolumeChartInstance.setOption(
+ JSON.parse(JSON.stringify(salesVolumeChartOption.value))
+ );
}
// 鏇存柊閿�鍞噾棰濊秼鍔垮浘琛�
@@ -903,6 +1283,11 @@
salesAreaChartInstance.setOption(salesAreaChartOption.value);
}
+ // 鏇存柊鏂板瀹㈡埛瓒嬪娍鍥捐〃
+ if (productTypeTrendChartInstance) {
+ productTypeTrendChartInstance.setOption(productTypeTrendChartOption.value);
+ }
+
// 鏇存柊绱閿�閲忚秼鍔垮浘琛�
if (cumulativeSalesVolumeChartInstance) {
cumulativeSalesVolumeChartInstance.setOption(
@@ -918,8 +1303,204 @@
}
};
+ // 琛ㄦ牸鍔ㄧ敾鎺у埗
+ let blockScrollTimer = null;
+ let boardScrollTimer = null;
+ let blockCurrentIndex = 0;
+ let boardCurrentIndex = 0;
+
+ const startBlockTableScroll = () => {
+ if (blockScrollTimer) {
+ clearInterval(blockScrollTimer);
+ }
+
+ const scrollTable = () => {
+ if (!blockTableBody.value || blockSalesData.value.length === 0) return;
+
+ const rows = blockTableBody.value.querySelectorAll("tr");
+ if (rows.length === 0) return;
+
+ const rowHeight = rows[0].offsetHeight;
+
+ blockTableBody.value.style.transition = "transform 0.5s ease-in-out";
+ blockTableBody.value.style.transform = `translateY(-${rowHeight}px)`;
+
+ setTimeout(() => {
+ blockTableBody.value.style.transition = "none";
+ blockTableBody.value.style.transform = "translateY(0)";
+
+ const firstItem = blockSalesData.value[0];
+ blockSalesData.value.shift();
+ blockSalesData.value.push(firstItem);
+ }, 500);
+ };
+
+ blockScrollTimer = setInterval(scrollTable, 2000);
+ };
+
+ const startBoardTableScroll = () => {
+ if (boardScrollTimer) {
+ clearInterval(boardScrollTimer);
+ }
+
+ const scrollTable = () => {
+ if (!boardTableBody.value || boardSalesData.value.length === 0) return;
+
+ const rows = boardTableBody.value.querySelectorAll("tr");
+ if (rows.length === 0) return;
+
+ const rowHeight = rows[0].offsetHeight;
+
+ boardTableBody.value.style.transition = "transform 0.5s ease-in-out";
+ boardTableBody.value.style.transform = `translateY(-${rowHeight}px)`;
+
+ setTimeout(() => {
+ boardTableBody.value.style.transition = "none";
+ boardTableBody.value.style.transform = "translateY(0)";
+
+ const firstItem = boardSalesData.value[0];
+ boardSalesData.value.shift();
+ boardSalesData.value.push(firstItem);
+ }, 500);
+ };
+
+ boardScrollTimer = setInterval(scrollTable, 2000);
+ };
+
+ const stopTableScroll = () => {
+ if (blockScrollTimer) {
+ clearInterval(blockScrollTimer);
+ blockScrollTimer = null;
+ }
+ if (boardScrollTimer) {
+ clearInterval(boardScrollTimer);
+ boardScrollTimer = null;
+ }
+ };
+
+ // 澶勭悊鏃堕棿缁村害閫夋嫨
+ const handleBlockTimeDimensionChange = dimension => {
+ blockTimeDimension.value = dimension;
+ generateBlockSalesData();
+ };
+
+ const handleBoardTimeDimensionChange = dimension => {
+ boardTimeDimension.value = dimension;
+ generateBoardSalesData();
+ };
+
+ // 澶勭悊浜у搧绫诲瀷閫夋嫨
+ const handleBlockProductTypeChange = type => {
+ blockProductType.value = type;
+ generateBlockSalesData();
+ };
+
+ const handleBoardProductTypeChange = type => {
+ boardProductType.value = type;
+ generateBoardSalesData();
+ };
+
+ // 澶勭悊閿�鍞尯閫夋嫨
+ const handleBlockAreaChange = area => {
+ blockSelectedArea.value = area;
+ generateBlockSalesData();
+ };
+
+ const handleBoardAreaChange = area => {
+ boardSelectedArea.value = area;
+ generateBoardSalesData();
+ };
+
+ // 澶勭悊鏂板瀹㈡埛瓒嬪娍鏃堕棿缁村害鍒囨崲
+ const handleCustomerTimeDimensionChange = dimension => {
+ customerTimeDimension.value = dimension;
+ updateCharts();
+ };
+
+ // 鐢熸垚鐮屽潡閿�鍞暟鎹�
+ const generateBlockSalesData = () => {
+ const data = [];
+ const year = 2024;
+
+ if (blockTimeDimension.value === "year") {
+ // 骞村害鏁版嵁锛�12涓湀
+ for (let month = 1; month <= 12; month++) {
+ const period = `${year}-${month.toString().padStart(2, "0")}`;
+ data.push({
+ period: period,
+ area: blockSelectedArea.value,
+ productType: blockProductType.value,
+ sales: Math.floor(Math.random() * 500) + 800,
+ sort: month,
+ });
+ }
+ } else {
+ // 鏈堝害鏁版嵁锛�30澶�
+ const month = 1;
+ for (let day = 1; day <= 30; day++) {
+ const period = `${year}-${month.toString().padStart(2, "0")}-${day
+ .toString()
+ .padStart(2, "0")}`;
+ data.push({
+ period: period,
+ area: blockSelectedArea.value,
+ productType: blockProductType.value,
+ sales: Math.floor(Math.random() * 50) + 20,
+ sort: day,
+ });
+ }
+ }
+
+ blockSalesData.value = data;
+ // 鏇存柊鍥捐〃
+ updateCharts();
+ };
+
+ // 鐢熸垚鏉挎潗閿�鍞暟鎹�
+ const generateBoardSalesData = () => {
+ const data = [];
+ const year = 2024;
+
+ if (boardTimeDimension.value === "year") {
+ // 骞村害鏁版嵁锛�12涓湀
+ for (let month = 1; month <= 12; month++) {
+ const period = `${year}-${month.toString().padStart(2, "0")}`;
+ data.push({
+ period: period,
+ area: boardSelectedArea.value,
+ productType: boardProductType.value,
+ sales: Math.floor(Math.random() * 400) + 600,
+ sort: month,
+ });
+ }
+ } else {
+ // 鏈堝害鏁版嵁锛�30澶�
+ const month = 1;
+ for (let day = 1; day <= 30; day++) {
+ const period = `${year}-${month.toString().padStart(2, "0")}-${day
+ .toString()
+ .padStart(2, "0")}`;
+ data.push({
+ period: period,
+ area: boardSelectedArea.value,
+ productType: boardProductType.value,
+ sales: Math.floor(Math.random() * 40) + 15,
+ sort: day,
+ });
+ }
+ }
+
+ boardSalesData.value = data;
+ // 鏇存柊鍥捐〃
+ updateCharts();
+ };
+
// 鐩戝惉绐楀彛澶у皬鍙樺寲
const handleResize = () => {
+ console.log("resize");
+ // 鍏堟洿鏂板浘琛ㄩ�夐」锛岄噸鏂拌绠楀搷搴斿紡鍊�
+ updateCharts();
+ // 鐒跺悗璋冩暣鍥捐〃澶у皬
if (salesVolumeChartInstance) {
salesVolumeChartInstance.resize();
}
@@ -932,6 +1513,9 @@
if (salesAreaChartInstance) {
salesAreaChartInstance.resize();
}
+ if (productTypeTrendChartInstance) {
+ productTypeTrendChartInstance.resize();
+ }
if (cumulativeSalesVolumeChartInstance) {
cumulativeSalesVolumeChartInstance.resize();
}
@@ -942,6 +1526,13 @@
// 鐢熷懡鍛ㄦ湡
onMounted(() => {
+ // 鍚姩椤堕儴鏍忔椂闂村埛鏂�
+ if (!timeTicker) {
+ timeTicker = setInterval(() => {
+ now.value = dayjs();
+ }, 1000);
+ }
+
// 璁剧疆榛樿鏃ユ湡鑼冨洿涓烘渶杩�3涓湀
const endDate = dayjs();
const startDate = endDate.subtract(3, "month");
@@ -950,13 +1541,21 @@
endDate.format("YYYY-MM-DD"),
];
+ // 鐢熸垚鍒濆鏁版嵁
+ generateBlockSalesData();
+ generateBoardSalesData();
+
// 绛夊緟DOM鏇存柊鍚庡垵濮嬪寲鍥捐〃
nextTick(() => {
initCharts();
+ // 鍚姩琛ㄦ牸婊氬姩鍔ㄧ敾
+ startBlockTableScroll();
+ startBoardTableScroll();
});
// 娣诲姞绐楀彛澶у皬鍙樺寲鐩戝惉
window.addEventListener("resize", handleResize);
+ document.addEventListener("fullscreenchange", handleFullscreenChange);
});
// 鑾峰彇浜у搧绫诲瀷鏍囩绫诲瀷
@@ -983,6 +1582,11 @@
// 缁勪欢鍗歌浇鏃堕攢姣佸浘琛ㄥ疄渚�
onBeforeUnmount(() => {
+ if (timeTicker) {
+ clearInterval(timeTicker);
+ timeTicker = null;
+ }
+
if (salesVolumeChartInstance) {
salesVolumeChartInstance.dispose();
}
@@ -995,6 +1599,10 @@
if (salesAreaChartInstance) {
salesAreaChartInstance.dispose();
}
+
+ if (productTypeTrendChartInstance) {
+ productTypeTrendChartInstance.dispose();
+ }
if (cumulativeSalesVolumeChartInstance) {
cumulativeSalesVolumeChartInstance.dispose();
}
@@ -1004,301 +1612,671 @@
// 绉婚櫎绐楀彛澶у皬鍙樺寲鐩戝惉
window.removeEventListener("resize", handleResize);
+ document.removeEventListener("fullscreenchange", handleFullscreenChange);
});
</script>
<style scoped>
- /* 澶栭儴瀹瑰櫒 - 鍗犳嵁鏁翠釜瑙嗗彛 */
.sales-statistics-container {
position: relative;
width: 100%;
- /* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
- min-height: calc(100vh - 84px);
- background-color: #f5f7fa;
+ min-height: calc(100vh - 8.4vh);
overflow: hidden;
+ color: #b8c8e0;
+ background: #041026;
}
- /* 鍐呴儴鍐呭鍖哄煙 - 鑷�傚簲瀹藉害 */
- .data-dashboard {
+ .sales-statistics-container.is-fullscreen {
+ min-height: 100vh;
+ height: 100vh;
+ }
+
+ /* 娣辫壊鑳屾櫙鍥� */
+ .bi-bg {
+ position: absolute;
+ inset: 0;
+ /* background-image: url("@/assets/BI/backImage@2x.png"); */
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ z-index: 0;
+ }
+
+ /* 椤堕儴鏍囬鏍� */
+ .bi-topbar {
position: relative;
+ z-index: 2;
+ height: 5.8vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .bi-topbar-title-bg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 8vh;
width: 100%;
- min-height: 100%;
- background-color: #ffffff;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
+ object-fit: cover;
+ z-index: 0;
+ pointer-events: none;
}
- .filter-area {
- padding: 20px;
- background-color: #ffffff;
- border-bottom: 1px solid #e4e7ed;
- display: flex;
- gap: 40px;
- align-items: center;
- flex-wrap: wrap;
- }
-
- .filter-section {
- display: flex;
- align-items: center;
- gap: 10px;
- }
-
- .filter-label {
- font-size: 14px;
- font-weight: 500;
- color: #303133;
- white-space: nowrap;
- }
-
- .dashboard-content {
+ .bi-topbar-content {
position: relative;
z-index: 1;
+ width: 100%;
+ padding: 0 2.8vh;
display: flex;
- flex-direction: column;
- gap: 20px;
- padding: 20px;
- min-height: 800px;
- overflow: hidden;
+ align-items: center;
+ justify-content: center;
}
- /* 琛屽竷灞� */
- .row {
+ .bi-topbar-title {
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 2.6vh;
+ font-weight: 800;
+ letter-spacing: 0.1vh;
+ background: linear-gradient(180deg, #ffffff 0%, #b8dfff 100%);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ color: transparent;
+ text-shadow: 0 0 2.6vh rgba(0, 164, 237, 0.55);
+ }
+
+ .bi-topbar-left {
+ position: absolute;
+ left: 1vh;
display: flex;
- gap: 20px;
- align-items: stretch;
+ align-items: center;
+ gap: 0.8vh;
+ color: rgba(208, 231, 255, 0.85);
+ font-size: 1.3vh;
}
- /* 绗竴琛岋細4涓寚鏍囧崱鐗� */
- .row-1 {
- height: 180px;
+ .status-sun {
+ color: #ffd85e;
+ text-shadow: 0 0 1vh rgba(255, 216, 94, 0.8);
+ font-size: 1.3vh;
+ line-height: 1;
}
- /* 绗簩琛岋細2涓秼鍔垮浘琛� */
- .row-2 {
- height: 350px;
- }
-
- /* 绗笁琛岋細绱鏁版嵁瓒嬪娍 */
- .row-3 {
- height: 350px;
- }
-
- /* 绗洓琛岋細琛ㄦ牸鍜屽浘琛� */
- .row-4 {
- height: 600px;
- }
-
- /* 鍗$墖鏍峰紡 */
- .panel-card {
- background-color: #ffffff;
- border-radius: 8px;
- border: 1px solid #e4e7ed;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
- transition: all 0.3s ease;
- }
-
- .panel-card:hover {
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
- transform: translateY(-2px);
- }
-
- /* 鍗$墖甯冨眬 */
- .card-1 {
- flex: 1;
- }
-
- .card-2 {
- flex: 1;
- }
-
- .card-3 {
- flex: 1;
- }
-
- .card-4 {
- flex: 1;
- }
-
- .card-5 {
- flex: 1;
- }
-
- .card-6 {
- flex: 1;
- }
-
- .card-7 {
- flex: 1;
- }
-
- .card-8 {
- flex: 1;
- }
-
- .card-9 {
- flex: 1;
- }
-
- .card-10 {
- flex: 1;
- }
-
- .card-11 {
- flex: 1;
- }
-
- .panel-title {
- padding: 15px 20px;
- font-size: 16px;
+ .bi-topbar-meta {
+ position: absolute;
+ right: 5.2vh;
+ /* top: 1.6vh; */
+ font-size: 1.2vh;
font-weight: 500;
- color: #303133;
- border-bottom: 1px solid #e4e7ed;
- background-color: #fafafa;
+ letter-spacing: 0.05vh;
+ color: rgba(208, 231, 255, 0.85);
+ display: flex;
+ align-items: center;
+ gap: 1vh;
}
- .card-1 .panel-title {
- border-left: 4px solid #409eff;
+ .fullscreen-btn {
+ position: absolute;
+ bottom: -1vh;
+ transform: none;
+ border: 0.1vh solid rgba(64, 158, 255, 0.45);
+ background: rgba(0, 164, 237, 0.14);
+ color: #d0e7ff;
+ width: 3.4vh;
+ height: 3.4vh;
+ border-radius: 0.6vh;
+ padding: 0;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ z-index: 10;
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
- .card-2 .panel-title {
- border-left: 4px solid #67c23a;
+ .fullscreen-btn:hover {
+ background: rgba(0, 164, 237, 0.24);
+ box-shadow: 0 0 1.2vh rgba(0, 164, 237, 0.3);
}
- .card-3 .panel-title {
- border-left: 4px solid #e6a23c;
+ .bi-topbar-sep {
+ opacity: 0.7;
}
- .card-4 .panel-title {
- border-left: 4px solid #f56c6c;
+ /* 涓讳綋缃戞牸甯冨眬 */
+ .bi-dashboard-grid {
+ position: relative;
+ z-index: 2;
+ height: calc(100vh - 8.4vh - 5.8vh);
+ min-height: 45vh;
+ padding: 1vh 1.8vh 1.4vh;
+ display: grid;
+ grid-template-columns: 1fr 1.05fr 1fr;
+ grid-template-rows: 1fr 1fr;
+ gap: 1.2vh;
}
- .card-5 .panel-title {
- border-left: 4px solid #409eff;
+ .sales-statistics-container.is-fullscreen .bi-dashboard-grid {
+ height: calc(100vh - 5.8vh);
}
- .card-6 .panel-title {
- border-left: 4px solid #67c23a;
+ .bi-panel {
+ background: rgba(3, 18, 46, 0.62);
+ border: 0.1vh solid rgba(64, 158, 255, 0.35);
+ border-radius: 0.4vh;
+ overflow: hidden;
+ box-shadow: 0 0 2.2vh rgba(0, 164, 237, 0.12);
+ display: flex;
+ flex-direction: column;
+ position: relative;
}
- .card-7 .panel-title {
- border-left: 4px solid #e6a23c;
+ .bi-panel-title {
+ height: 4.4vh;
+ display: flex;
+ align-items: center;
+ padding: 0 1.8vh;
+ font-size: 1.5vh;
+ font-weight: 700;
+ color: #b8c8e0;
+ background: linear-gradient(
+ 90deg,
+ rgba(0, 164, 237, 0.2),
+ rgba(0, 164, 237, 0.04)
+ );
+ border-bottom: 0.1vh solid rgba(64, 158, 255, 0.25);
}
- .card-8 .panel-title {
- border-left: 4px solid #f56c6c;
+ .panel-tabs,
+ .panel-tabs2 {
+ position: absolute;
+ top: 0.8vh;
+ display: flex;
+ gap: 0.6vh;
+ z-index: 4;
}
- .card-9 .panel-title {
- border-left: 4px solid #409eff;
+ .panel-tabs {
+ right: 1.2vh;
}
- .card-10 .panel-title {
- border-left: 4px solid #67c23a;
+ .panel-tabs2 {
+ right: 8vh;
+ }
+ .tab-item {
+ font-size: 1.2vh;
+ color: rgba(184, 200, 224, 0.75);
+ padding: 0.1vh 0.5vh;
+ border: 0.1vh solid rgba(64, 158, 255, 0.25);
+ border-radius: 0.3vh;
+ line-height: 1.4;
+ cursor: pointer;
}
- .card-11 .panel-title {
- border-left: 4px solid #e6a23c;
+ .tab-item.active {
+ color: #ffffff;
+ border-color: rgba(0, 164, 237, 0.65);
+ background: rgba(0, 164, 237, 0.22);
}
- .chart-container {
+ .bi-panel-body {
flex: 1;
- padding: 20px;
+ padding: 0.8vh 1vh;
+ position: relative;
}
- .table-container {
- flex: 1;
- padding: 20px;
+ .scroll-table-container {
+ height: 33vh;
+ overflow: hidden;
+ position: relative;
+ }
+
+ .scroll-table {
+ width: 100%;
+ border-collapse: collapse;
+ color: #b8c8e0;
+ }
+
+ .scroll-table th {
+ /* background-color: #0e2a54; */
+ padding: 1.2vh;
+ text-align: left;
+ font-size: 1.2vh;
+ font-weight: bold;
+ border: 1px solid rgba(184, 200, 224, 0.2);
+ }
+
+ .scroll-table td {
+ padding: 1vh;
+ font-size: 1.1vh;
+ border-bottom: 1px solid rgba(184, 200, 224, 0.1);
+ }
+
+ .scroll-table tbody {
+ display: block;
+ height: 35vh;
+ overflow: hidden;
+ }
+
+ .scroll-table thead,
+ .scroll-table tbody tr {
+ display: table;
+ width: 100%;
+ table-layout: fixed;
+ }
+
+ .scroll-table th,
+ .scroll-table td {
+ width: 25%;
+ box-sizing: border-box;
+ font-size: 1.4vh;
+ }
+
+ .scroll-table tbody tr {
+ transition: all 0.5s ease-in-out;
+ }
+
+ /* .scroll-table tbody tr:nth-child(odd) {
+ background-color: rgba(64, 158, 255, 0.05);
+ }
+
+ .scroll-table tbody tr:nth-child(even) {
+ background-color: rgba(64, 158, 255, 0.1);
+ } */
+ .oddTableTr {
+ background-color: rgba(64, 158, 255, 0.05);
+ }
+ .evenTableTr {
+ background-color: rgba(64, 158, 255, 0.1);
+ }
+
+ .scroll-table-container:hover tbody {
+ overflow: hidden;
+ }
+
+ .echart-fill {
+ width: 100%;
+ height: 100%;
+ }
+
+ .chart-filter-tabs {
+ display: flex;
+ gap: 0.6vh;
+ margin: 0 0 0.5vh 0;
+ justify-self: end;
+ }
+
+ .cf-tab {
+ font-size: 1.1vh;
+ color: rgba(184, 200, 224, 0.68);
+ background: rgba(18, 56, 106, 0.65);
+ border: 0.1vh solid rgba(64, 158, 255, 0.25);
+ padding: 0.3vh 0.9vh;
+ line-height: 1;
+ cursor: pointer;
+ }
+
+ .cf-tab.active {
+ color: #d9ecff;
+ background: rgba(0, 108, 208, 0.85);
+ border-color: rgba(64, 158, 255, 0.65);
+ }
+
+ .chart-unit-row {
+ display: flex;
+ justify-content: space-between;
+ font-size: 1.2vh;
+ color: rgba(208, 231, 255, 0.88);
+ margin-bottom: 0.4vh;
+ padding: 0 0.2vh;
+ }
+
+ .dot-legend::before {
+ content: "";
+ display: inline-block;
+ width: 0.8vh;
+ height: 0.8vh;
+ background: #65a0ff;
+ margin-right: 0.6vh;
+ }
+
+ .chart-mini-title {
+ display: flex;
+ align-items: center;
+ gap: 0.8vh;
+ font-size: 1.8vh;
+ color: #d9ecff;
+ }
+
+ /* 闈㈡澘搴曢儴鍚堣琛� */
+ .panel-summary-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.8vh 1.2vh;
+ /* margin-top: 0.8vh; */
+ background: #041e3c;
+ border-top: 0.1vh solid rgba(64, 158, 255, 0.25);
+ border-radius: 0 0 0.4vh 0.4vh;
+ width: 100%;
+ position: absolute;
+ bottom: 0;
+ }
+
+ .summary-label {
+ font-size: 1.3vh;
+ font-weight: 700;
+ color: #b8c8e0;
+ }
+
+ .summary-value {
+ font-size: 1.4vh;
+ font-weight: 800;
+ color: #00a4ed;
+ margin-right: 3.8vh;
+ text-shadow: 0 0 1vh rgba(0, 164, 237, 0.5);
+ }
+
+ .summary-value2 {
+ font-size: 1.4vh;
+ font-weight: 800;
+ color: #00a4ed;
+ margin-right: 5.8vh;
+ text-shadow: 0 0 1vh rgba(0, 164, 237, 0.5);
+ }
+ .diamond {
+ width: 1vh;
+ height: 1vh;
+ background: #1e8bff;
+ transform: rotate(45deg);
+ display: inline-block;
+ }
+
+ .chart-unit-single {
+ justify-content: flex-start;
+ margin-bottom: 0.2vh;
+ }
+
+ .bi-panel-top-left .echart-fill,
+ .bi-panel-top-right .echart-fill {
+ height: calc(100% - 4.4vh);
+ }
+
+ .bi-panel-bottom-left .echart-fill,
+ .bi-panel-bottom-right .echart-fill {
+ height: calc(100% - 2.8vh);
+ }
+
+ .bi-panel-bottom-center .echart-fill {
+ height: calc(100% - 4.4vh);
+ }
+
+ .bi-panel-top-left {
+ grid-column: 1;
+ grid-row: 1;
+ position: relative;
+ }
+
+ .bi-panel-top-right {
+ grid-column: 3;
+ grid-row: 1;
+ position: relative;
+ }
+
+ .bi-panel-bottom-left {
+ grid-column: 1;
+ grid-row: 2;
+ overflow-y: auto;
+ }
+ .bi-panel-bottom-left::-webkit-scrollbar {
+ width: 0vh;
+ height: 0vh;
+ }
+
+ .bi-panel-bottom-center {
+ grid-column: 2;
+ grid-row: 2;
+ }
+
+ .bi-panel-bottom-right {
+ grid-column: 3;
+ grid-row: 2;
+ overflow-y: auto;
+ }
+ .bi-panel-bottom-right::-webkit-scrollbar {
+ width: 0vh;
+ height: 0vh;
+ }
+
+ /* 涓績鐜诞灞傦紙缁濆瀹氫綅鍦ㄧ綉鏍间笂鏂癸級 */
+ .center-ring {
+ grid-column: 2;
+ grid-row: 1 / span 2;
+ position: absolute;
+ background: url("@/assets/BI/imageSS@2x.png") no-repeat bottom center;
+ background-size: 100% 30%;
+ left: 25%;
+ top: 25%;
+ transform: translate(-50%, -50%);
+ width: 60vh;
+ height: 40.5vh;
+ z-index: 3;
+ pointer-events: none;
+ }
+ .center-ring-box {
+ position: absolute;
+ /* inset: 0; */
+ height: 100%;
+ width: 100%;
+ /* background-color: #fff; */
+ background: url("@/assets/BI/imageSStop.png") no-repeat center center;
+ background-size: 80% 90%;
+ }
+
+ .center-ring-bg {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ filter: drop-shadow(0 0 2vh rgba(0, 164, 237, 0.35));
+ }
+
+ .center-ring-content {
+ position: absolute;
+ inset: 0;
+ }
+
+ .center-ring-content::before,
+ .center-ring-content::after {
+ content: "";
+ position: absolute;
+ left: 50%;
+ top: 56%;
+ width: 37vh;
+ height: 14.6vh;
+ transform: translate(-50%, -50%) rotate(-18deg);
+ border: 0.2vh solid rgba(40, 186, 255, 0.45);
+ border-radius: 50%;
+ filter: drop-shadow(0 0 0.8vh rgba(0, 164, 237, 0.35));
+ opacity: 0.7;
+ }
+
+ .center-ring-content::after {
+ width: 36vh;
+ height: 15vh;
+ transform: translate(-50%, -50%) rotate(26deg);
+ border-color: rgba(80, 220, 255, 0.35);
+ opacity: 0.55;
+ }
+
+ .center-ring-title {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 3.6vh;
+ line-height: 1.05;
+ text-align: center;
+ font-weight: 900;
+ color: #eaf6ff;
+ text-shadow: 0 0 2.2vh rgba(0, 164, 237, 0.55);
+ z-index: 2;
+ }
+
+ .center-ring-title::before {
+ content: "";
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ width: 15.5vh;
+ height: 15.5vh;
+ transform: translate(-50%, -50%);
+ background: radial-gradient(
+ circle,
+ rgba(43, 199, 255, 0.26) 0%,
+ rgba(8, 28, 61, 0.86) 70%
+ );
+ border: 0.2vh solid rgba(39, 198, 255, 0.46);
+ border-radius: 50%;
+ box-shadow: 0 0 2vh rgba(0, 164, 237, 0.45),
+ inset 0 0 2.6vh rgba(0, 164, 237, 0.2);
+ z-index: -1;
+ }
+
+ .center-metric {
+ position: absolute;
+ width: 15.5vh;
+ z-index: 3;
+ text-align: center;
+ height: 12vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .center-metric-label {
+ font-size: 1.2vh;
+ font-weight: 500;
+ color: rgba(234, 246, 255, 0.9);
+ margin-top: 0;
+ }
+
+ .center-metric-value {
+ font-size: 3.4vh;
+ font-weight: 800;
+ color: #eaf6ff;
+ text-shadow: 0 0 0.8vh rgba(0, 229, 255, 0.22);
+ line-height: 1;
+ }
+
+ .center-metric-unit {
+ margin-top: 0;
+ font-size: 1.2vh;
+ color: rgba(208, 231, 255, 0.85);
+ }
+
+ .m1 {
+ top: 2.5vh;
+ left: 4.8vh;
+ text-align: left;
+ }
+
+ .m2 {
+ top: 4.1vh;
+ right: 8.6vh;
+ text-align: right;
+ }
+
+ .m3 {
+ bottom: 7.9vh;
+ left: 4vh;
+ text-align: left;
+ }
+
+ .m4 {
+ bottom: 7vh;
+ right: 5.4vh;
+ text-align: right;
+ }
+
+ @media (max-width: 1100px) {
+ .bi-topbar-content {
+ padding: 0 1.4vh;
+ }
+ .center-ring {
+ left: 45.2%;
+ width: 33vh;
+ height: 24.5vh;
+ top: 2.4vh;
+ }
+ .center-ring-title {
+ top: 50%;
+ font-size: 2.8vh;
+ transform: translate(-50%, -50%);
+ }
+ .center-metric {
+ height: 10.5vh;
+ }
+ .m1 {
+ top: 5.2vh;
+ left: 4.2vh;
+ }
+ .m2 {
+ top: 5.4vh;
+ right: 4.2vh;
+ }
+ .m3 {
+ bottom: 6.2vh;
+ left: 4.8vh;
+ }
+ .m4 {
+ bottom: 6.8vh;
+ right: 4.4vh;
+ }
+ }
+ .scroll-table-content {
+ height: 39vh;
overflow: auto;
}
-
- .stats-grid {
- flex: 1;
- padding: 15px;
+ .scroll-table-content::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+ background-color: transparent;
+ }
+ .total-row {
+ position: absolute;
+ bottom: 0vh;
+ left: 0;
+ width: 100%;
display: flex;
+ height: 3.5vh;
+ justify-content: space-around;
align-items: center;
- justify-content: center;
+ background-color: #081843;
}
- .stat-item {
- background-color: #fafafa;
- border-radius: 8px;
- padding: 15px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- border: 1px solid #e4e7ed;
- min-height: 80px;
- width: 100%;
+ .total-cell {
+ width: 20%;
+ font-size: 1.4vh;
+ margin-bottom: 0.5vh;
+ line-height: 3.5vh;
+ padding-left: 0.8vh;
+ color: #eaf6ff;
+ text-shadow: 0 0 0.8vh rgba(0, 229, 255, 0.22);
+ text-align: left;
+ color: #c3c3c3;
}
-
- .stat-value {
- font-size: 24px;
- font-weight: 600;
- color: #303133;
- margin-bottom: 5px;
+ .total-cell2 {
+ width: 20%;
+ font-size: 1.4vh;
+ margin-bottom: 0.5vh;
+ line-height: 3.5vh;
+ color: #eaf6ff;
+ text-shadow: 0 0 0.8vh rgba(0, 229, 255, 0.22);
+ text-align: left;
+ color: #c3c3c3;
}
-
- .sales-volume-color {
- color: #409eff;
- text-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
- }
-
- .sales-amount-color {
- color: #67c23a;
- text-shadow: 0 2px 4px rgba(103, 194, 58, 0.3);
- }
-
- .new-customer-color {
- color: #e6a23c;
- text-shadow: 0 2px 4px rgba(230, 162, 60, 0.3);
- }
-
- .total-customer-color {
- color: #f56c6c;
- text-shadow: 0 2px 4px rgba(245, 108, 108, 0.3);
- }
-
- .stat-unit {
- font-size: 12px;
- color: #909399;
- margin-bottom: 3px;
- }
-
- .stat-change {
- font-size: 12px;
- color: #67c23a;
- }
-
- /* 琛ㄦ牸鏍峰紡 */
- :deep(.el-table) {
- border-radius: 8px;
- overflow: hidden;
- }
-
- :deep(.el-table th) {
- background-color: #fafafa;
- font-weight: 500;
- }
-
- :deep(.el-table tr:hover > td) {
- background-color: #ecf5ff;
- }
-
- .data-value {
- font-weight: bold;
- color: #409eff;
- }
-
- /* 涓嬫媺閫夋嫨妗嗘牱寮� */
- :deep(.el-select) {
- width: 100%;
- }
-
- :deep(.el-date-picker) {
- width: 100%;
- }
-</style>
\ No newline at end of file
+</style>
--
Gitblit v1.9.3