From 5a80a60227011398c8fe306d4adcdd09297d21e2 Mon Sep 17 00:00:00 2001
From: zxl <763096477@qq.com>
Date: 星期四, 23 十月 2025 18:04:46 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/send_coupon' into send_coupon

---
 manager/src/views/statistics/order.vue |  406 ++++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 296 insertions(+), 110 deletions(-)

diff --git a/manager/src/views/statistics/order.vue b/manager/src/views/statistics/order.vue
index a931028..842b373 100644
--- a/manager/src/views/statistics/order.vue
+++ b/manager/src/views/statistics/order.vue
@@ -6,84 +6,117 @@
       </Card>
     </Affix>
 
-<!--    <Card class="card">-->
-<!--        <div class="chart-wrapper">-->
-<!--          <div class="chart-header">-->
-<!--            <h2>PV/UV 瓒嬪娍鍥�</h2>-->
-<!--          </div>-->
-<!--          <div ref="chartDom" class="chart-container"></div>-->
-<!--        </div>-->
-<!--    </Card>-->
-<!--    <Card class="card">-->
-<!--      <div class="my-chart-container">-->
-<!--        <div class="chart-wrapper" style="height: 800px">-->
-<!--          <div class="chart-header">-->
-<!--            <h2>鍟嗗搧/瑙嗛娴忚閲忓畬鎾巼 瓒嬪娍鍥�</h2>-->
-<!--          </div>-->
-<!--          <div class="button-group-wrapper">-->
-<!--            &lt;!&ndash; 绗竴缁勶細鍟嗗搧/瑙嗛锛堥潬宸︼級 &ndash;&gt;-->
-<!--            <ButtonGroup>-->
-<!--              <Button-->
-<!--                :type="currentType === 'goods' ? 'primary' : 'default'"-->
-<!--                @click="handleTypeChange('goods')"-->
-<!--              >-->
-<!--                鍟嗗搧-->
-<!--              </Button>-->
-<!--              <Button-->
-<!--                :type="currentType === 'video' ? 'primary' : 'default'"-->
-<!--                @click="handleTypeChange('video')"-->
-<!--              >-->
-<!--                瑙嗛-->
-<!--              </Button>-->
-<!--            </ButtonGroup>-->
+    <Card class="card">
+        <div class="chart-wrapper">
+          <div class="chart-header">
+            <h2>PV/UV 瓒嬪娍鍥�</h2>
+          </div>
+          <div ref="chartDom" class="chart-container"></div>
+        </div>
+    </Card>
+    <Card class="card">
+      <div class="my-chart-container">
+        <div class="chart-wrapper" style="height: 800px">
+          <div class="chart-header">
+            <h2>鍟嗗搧/瑙嗛娴忚閲忓畬鎾巼 鎺掑悕鍥�</h2>
+          </div>
+          <div class="button-group-wrapper">
+            <!-- 绗竴缁勶細鍟嗗搧/瑙嗛锛堥潬宸︼級 -->
+            <ButtonGroup>
+              <Button
+                :type="currentType === 'goods' ? 'primary' : 'default'"
+                @click="handleTypeChange('goods')"
+              >
+                鍟嗗搧
+              </Button>
+              <Button
+                :type="currentType === 'video' ? 'primary' : 'default'"
+                @click="handleTypeChange('video')"
+              >
+                瑙嗛
+              </Button>
+            </ButtonGroup>
 
-<!--            &lt;!&ndash; 绗簩缁勶細鍓�10/鍓�20/鍓�30锛堥潬鍙筹級 &ndash;&gt;-->
-<!--            <ButtonGroup>-->
-<!--              <Button-->
-<!--                :type="currentLimit === 10 ? 'success' : 'default'"-->
-<!--                @click="handleLimitChange(10)"-->
-<!--              >-->
-<!--                鍓�10-->
-<!--              </Button>-->
-<!--              <Button-->
-<!--                :type="currentLimit === 20 ? 'success' : 'default'"-->
-<!--                @click="handleLimitChange(20)"-->
-<!--              >-->
-<!--                鍓�20-->
-<!--              </Button>-->
-<!--              <Button-->
-<!--                :type="currentLimit === 30 ? 'success' : 'default'"-->
-<!--                @click="handleLimitChange(30)"-->
-<!--              >-->
-<!--                鍓�30-->
-<!--              </Button>-->
-<!--            </ButtonGroup>-->
-<!--          </div>-->
-<!--          <div ref="viewPrintChartDom" class="view-chart-container"></div>-->
-<!--        </div>-->
-<!--      </div>-->
-<!--    </Card>-->
-<!--    <Card class="card">-->
-<!--      <div class="my-chart-container">-->
-<!--        <div class="chart-wrapper">-->
-<!--          <div class="chart-header">-->
-<!--            <h2>璁㈠崟澧為暱 瓒嬪娍鍥�</h2>-->
-<!--          </div>-->
-<!--          <div ref="orderCountChartDom" class="chart-container"></div>-->
-<!--        </div>-->
-<!--      </div>-->
-<!--    </Card>-->
-<!--    <Card class="card">-->
-<!--      <div class="my-chart-container">-->
-<!--        <div class="chart-wrapper">-->
-<!--          <div class="chart-header">-->
-<!--            <h2>娴忚鏁版嵁/璁㈠崟鏃堕棿娈� 瓒嬪娍鍥�</h2>-->
-<!--          </div>-->
-<!--          <div ref="orderTimePeriodChartDom" class="chart-container"></div>-->
-<!--        </div>-->
-<!--      </div>-->
-
-<!--    </Card>-->
+            <!-- 绗簩缁勶細鍓�10/鍓�20/鍓�30锛堥潬鍙筹級 -->
+            <ButtonGroup>
+              <Button
+                :type="currentLimit === 10 ? 'success' : 'default'"
+                @click="handleLimitChange(10)"
+              >
+                鍓�10
+              </Button>
+              <Button
+                :type="currentLimit === 20 ? 'success' : 'default'"
+                @click="handleLimitChange(20)"
+              >
+                鍓�20
+              </Button>
+              <Button
+                :type="currentLimit === 30 ? 'success' : 'default'"
+                @click="handleLimitChange(30)"
+              >
+                鍓�30
+              </Button>
+            </ButtonGroup>
+          </div>
+          <div ref="viewPrintChartDom" class="view-chart-container"></div>
+        </div>
+      </div>
+    </Card>
+    <Card class="card">
+      <div class="my-chart-container">
+        <div class="chart-wrapper">
+          <div class="chart-header">
+            <h2>璁㈠崟澧為暱 瓒嬪娍鍥�</h2>
+          </div>
+          <div ref="orderCountChartDom" class="chart-container"></div>
+        </div>
+      </div>
+    </Card>
+    <Card class="card">
+      <div class="my-chart-container">
+        <div class="chart-wrapper">
+          <div class="chart-header">
+            <h2>娴忚鏁版嵁/璁㈠崟鏃堕棿娈� 瓒嬪娍鍥�</h2>
+          </div>
+          <div ref="orderTimePeriodChartDom" class="chart-container"></div>
+        </div>
+      </div>
+    </Card>
+    <Card class="card">
+      <div class="my-chart-container">
+        <div class="chart-wrapper" style="height: 800px">
+          <div class="chart-header">
+            <h2>鍟嗗搧澶嶈喘鐜� 鎺掑悕鍥�</h2>
+          </div>
+          <div class="button-group-wrapper">
+            <!-- 椤甸潰鏍峰紡闇�瑕� -->
+            <div></div>
+            <ButtonGroup>
+              <Button
+                :type="repurchaseCurrentLimit === 10 ? 'success' : 'default'"
+                @click="handleRepurchaseLimitChange(10)"
+              >
+                鍓�10
+              </Button>
+              <Button
+                :type="repurchaseCurrentLimit === 20 ? 'success' : 'default'"
+                @click="handleRepurchaseLimitChange(20)"
+              >
+                鍓�20
+              </Button>
+              <Button
+                :type="repurchaseCurrentLimit === 30 ? 'success' : 'default'"
+                @click="handleRepurchaseLimitChange(30)"
+              >
+                鍓�30
+              </Button>
+            </ButtonGroup>
+          </div>
+          <div ref="productRepurchaseRateChartDom" class="repurchase-chart-container"></div>
+        </div>
+      </div>
+    </Card>
 
 
     <Card class="card">
@@ -284,7 +317,7 @@
 import refundRow from "./order/refundOrder";
 import affixTime from "@/components/affix-time";
 import * as echarts from 'echarts';
-import {pvUvData,getOrderCount,getViewAndCompletionRateCount,getOrderTimePeriod} from "../../api/orderStatistics";
+import {pvUvData,getOrderCount,getViewAndCompletionRateCount,getOrderTimePeriod,gerProductRepurchase} from "../../api/orderStatistics";
 
 export default {
   components: { orderRow, refundRow, affixTime },
@@ -294,11 +327,16 @@
       // 鍥捐〃瀹炰緥
       chart: null, //pvuv
       orderCountChart:null, //璁㈠崟鏃ヨ秼鍔�
-      viewPrintChart:null,
-      orderTimePeriodChart:null,
+      viewPrintChart:null, //瀹屾挱鐜�
+      orderTimePeriodChart:null, //璁㈠崟/娴忚鏃堕棿
+      productRepurchaseRateChart:null,//鍟嗗搧澶嶈喘鐜�
 
       currentType:"goods",
       currentLimit:10,
+
+      repurchaseCurrentLimit:10,
+
+
       // 缁熻鏁版嵁
 
       orderOrRefund: 1, // 璁㈠崟杩樻槸閫�鍗�
@@ -621,8 +659,6 @@
      const dates = [];
      const today = new Date();
      let days = 1;
-     console.log(param.year)
-     console.log(param.month)
      if(param.year !== null && param.year !== undefined && param.month !== null && param.month !== undefined && param.month !=='' ){
        // 褰撲紶鍏ュ勾浠藉拰鏈堜唤鏃讹紝鐢熸垚璇ユ湀浠界殑鎵�鏈夋棩鏈�
        const year = parseInt(param.year);
@@ -672,28 +708,34 @@
       if (this.orderTimePeriodChart){
         this.orderTimePeriodChart.resize();
       }
+      if (this.productRepurchaseRateChart){
+        this.productRepurchaseRateChart.resize();
+      }
     },
     initChart(){
       this.chart = echarts.init(this.$refs.chartDom)
       this.viewPrintChart = echarts.init(this.$refs.viewPrintChartDom)
       this.orderCountChart = echarts.init(this.$refs.orderCountChartDom)
       this.orderTimePeriodChart = echarts.init(this.$refs.orderTimePeriodChartDom)
+      this.productRepurchaseRateChart = echarts.init(this.$refs.productRepurchaseRateChartDom)
 
-      this.updateChartData(this.orderParams)
 
-      this.updateOrderCountChartDate(this.orderParams)
+      this.updateChartData(this.orderParams)//鏇存柊 pvuv
 
-      let form = {...this.orderParams};
-      console.log(this.currentType)
-      console.log(this.currentLimit)
-      form.currentType = this.currentType;
-      form.currentLimit = this.currentLimit;
-      console.log(form)
-      this.updateViewAndCompletionRateData(form)
+      this.updateOrderCountChartDate(this.orderParams)//鏇存柊鏃ヨ鍗�
 
-      this.updateOrderTimePeriodData(this.orderParams)
+      let viewForm = {...this.orderParams};
+      viewForm.currentType = this.currentType;
+      viewForm.currentLimit = this.currentLimit;
+      this.updateViewAndCompletionRateData(viewForm)//鏇存柊瀹屾挱鐜�
+
+      this.updateOrderTimePeriodData(this.orderParams)//鏇存柊娴忚鏁版嵁锛屼笅璁㈠崟鏃ユ湡鏃堕棿娈靛垎鏋�
+
+      let productRepurchaseDataForm ={...this.orderParams};
+      productRepurchaseDataForm.currentLimit = this.repurchaseCurrentLimit
+      this.updateProductRepurchaseData(productRepurchaseDataForm)//
     },
-    // 鏇存柊鍥捐〃鏁版嵁
+    //------------鏇存柊鍥捐〃鏁版嵁pvuv start
     async updateChartData(param) {
       // 鐢熸垚鏃ユ湡鍜孭V鏁版嵁
       const { pvData ,uvData} = await this.generateChartData(param)
@@ -851,8 +893,8 @@
       // 3. 姝ゆ椂pvData鍜寀vData宸茶鎺ュ彛鏁版嵁濉厖锛屽啀杩斿洖
       return { pvData, uvData };
     },
-    //-------------
-    //-------------璁㈠崟鏃ヨ秼鍔�
+    //-------------end
+    //-------------璁㈠崟鏃ヨ秼鍔縮tart
     async updateOrderCountChartDate(param){
      const { orderCountData } = await this.generateOrderCountChartData(param)
       this.orderCountChart.setOption(this.getOrderCountChartOption(this.getDates(param),orderCountData))
@@ -954,9 +996,9 @@
         animationEasing: 'cubicOut' // 缂撳姩鏁堟灉锛屼紭鍖栧姩鐢绘祦鐣呭害
       };
     },
-    //-------------
+    //-------------end
 
-    //-------------娴忚閲忥紝瀹屾挱鐜囷紝
+    //-------------娴忚閲忥紝瀹屾挱鐜囷紝start
     handleLimitChange(limit) {
       if (this.currentLimit !== limit) {
         this.currentLimit = limit
@@ -1028,7 +1070,7 @@
         grid: {
           left: '4%', // 瑙嗛绫诲瀷宸︾Щ锛岀粰瀹屾挱鐜囨爣绛剧暀绌洪棿
           right: '12%',
-          bottom: '0',
+          bottom: '2%',
           top: '5%',
           containLabel: true
         },
@@ -1043,6 +1085,7 @@
           min: 0
         },
         yAxis: {
+          inverse: true,
           type: 'category',
           data: yData,
           axisLine: { lineStyle: { color: '#ddd' } },
@@ -1090,8 +1133,8 @@
         ]
       };
     },
-    //-------------
-    //-------------娴忚鏁版嵁锛屼笅璁㈠崟鏃ユ湡鏃堕棿娈靛垎鏋�
+    //-------------end
+    //-------------娴忚鏁版嵁锛屼笅璁㈠崟鏃ユ湡鏃堕棿娈靛垎鏋恠tart
     async updateOrderTimePeriodData(param){
      const { viewData,countData,dateData} = await this.generateOrderTimePeriodChartData(param);
       this.orderTimePeriodChart.setOption(this.getOrderTimePeriodChartOption(viewData,countData,dateData));
@@ -1243,7 +1286,141 @@
         animationEasing: 'cubicOut'
       };
     },
+    //--------------ebd
 
+    //-------------澶嶈喘鐜� start
+    handleRepurchaseLimitChange(limit) {
+      if (this.repurchaseCurrentLimit !== limit) {
+        this.repurchaseCurrentLimit = limit
+        let form = {...this.orderParams}
+        form.currentLimit = this.repurchaseCurrentLimit;
+        this.updateProductRepurchaseData(form) // 閲嶆柊鍔犺浇鏁版嵁
+      }
+    },
+    async updateProductRepurchaseData(param){
+      const { rateData,goodsData} = await this.generateProductRepurchaseChartData(param);
+      this.productRepurchaseRateChart.setOption(this.getProductRepurchaseChartOption(this.getDates(param),rateData,goodsData));
+    },
+    async generateProductRepurchaseChartData(param){
+      let rateData = [];
+      let goodsData = [];
+      try{
+        const res =  await gerProductRepurchase(param);
+        if (res.code === 200){
+          rateData = res.data.rateData;
+          goodsData = res.data.goodsData;
+        }
+      }catch (error){
+        console.error("鎺ュ彛璋冪敤澶辫触", error);
+      }
+      return {rateData,goodsData}
+    },
+    getProductRepurchaseChartOption(dates, rateData, goodsData) {
+      // 宸ュ叿鍑芥暟锛氬垽鏂暟鍊兼槸鍚︿负鏁存暟锛堥伩鍏�100.00鏄剧ず涓�100.00%锛�
+      const isInteger = (num) => {
+        return Math.abs(num - Math.round(num)) < Number.EPSILON;
+      };
+
+      return {
+        tooltip: {
+          trigger: 'axis',
+          backgroundColor: 'rgba(255, 255, 255, 0.9)',
+          borderColor: '#eee',
+          borderWidth: 1,
+          textStyle: { color: '#333' },
+          axisPointer: {
+            type: 'shadow'
+          },
+          formatter: (params) => {
+            const goodsName = params[0].name;
+            const rate = params[0].value;
+            // 鏁存暟鏄剧ず鏃犲皬鏁帮紝闈炴暣鏁颁繚鐣欎袱浣嶅皬鏁�
+            const displayRate = isInteger(rate) ? rate : rate.toFixed(2);
+            return `<div class="font-medium">${goodsName}</div>
+            <div>澶嶈喘鐜�: <span style="color: #722ED1; font-weight: bold">${displayRate}%</span></div>`;
+          },
+        },
+        grid: {
+          left: '3%',
+          right: '4%',
+          bottom: '3%',
+          top: '3%',
+          containLabel: true
+        },
+        xAxis: {
+          type: 'value',
+          axisLine: { show: false },
+          axisTick: { show: false },
+          axisLabel: {
+            color: '#86909C',
+            // 鍧愭爣杞存爣绛撅細鏁存暟鏄剧ず鏃犲皬鏁帮紝闈炴暣鏁颁繚鐣�0浣嶏紙鍙栨暣锛�
+            formatter: (value) => {
+              return isInteger(value) ? `${value}%` : `${Math.round(value)}%`;
+            }
+          },
+          splitLine: {
+            lineStyle: {
+              color: '#f2f3f5',
+              type: 'dashed'
+            }
+          },
+          min: 0,
+          max: (value) => {
+            // 闄愬埗鏈�澶у�间笉瓒呰繃100锛堝璐巼鏈�楂�100%锛夛紝鍚屾椂淇濈暀1.2鍊嶇殑闂磋窛
+            const calculatedMax = value.max * 1.2;
+            return Math.min(calculatedMax, 100);
+          }
+        },
+        yAxis: {
+          type: 'category',
+          data: goodsData,
+          axisLine: { lineStyle: { color: '#eee' } },
+          axisTick: { show: false },
+          axisLabel: {
+            color: '#86909C',
+            interval: 0,
+            formatter: (value) => {
+              return value.length > 10 ? value.substring(0, 10) + '...' : value;
+            }
+          },
+          splitLine: { show: false },
+          inverse: true
+        },
+        series: [
+          {
+            name: '鍟嗗搧澶嶈喘鐜�',
+            type: 'bar',
+            data: rateData,
+            barWidth: '60%',
+            itemStyle: {
+              color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+                { offset: 0, color: '#722ED1' },
+                { offset: 1, color: '#EB2F96' }
+              ]),
+              borderRadius: [0, 4, 4, 0]
+            },
+            emphasis: {
+              itemStyle: {
+                shadowBlur: 10,
+                shadowColor: 'rgba(114, 46, 209, 0.5)'
+              }
+            },
+            label: {
+              show: true,
+              position: 'right',
+              // 鏌辩姸鍥炬爣绛撅細鏁存暟鏄剧ず鏃犲皬鏁帮紝闈炴暣鏁颁繚鐣�1浣�
+              formatter: (params) => {
+                const value = params.value;
+                return isInteger(value) ? `${value}%` : value.toFixed(1) + '%';
+              },
+              color: '#722ED1',
+              fontWeight: 'bold'
+            }
+          }
+        ],
+      };
+    },
+    //-------------end
     formatDate(date) {
       const month = date.getMonth() + 1; // 鏈堜唤浠�0寮�濮嬶紝闇�+1
       const day = date.getDate();
@@ -1311,15 +1488,20 @@
 
       //鏇存柊琛ㄦ牸
 
-      // this.updateChartData(this.orderParams)
-      // this.updateOrderCountChartDate(this.orderParams)
-      //
-      // let form = {...this.orderParams};
-      // form.currentType = this.currentType;
-      // form.currentLimit = this.currentLimit;
-      // this.updateViewAndCompletionRateData(form)
-      //
-      // this.updateOrderTimePeriodData(this.orderParams)
+      this.updateChartData(this.orderParams)
+      this.updateOrderCountChartDate(this.orderParams)
+
+      let form = {...this.orderParams};
+      form.currentType = this.currentType;
+      form.currentLimit = this.currentLimit;
+      this.updateViewAndCompletionRateData(form)
+
+      this.updateOrderTimePeriodData(this.orderParams)
+
+      let productRepurchaseDataForm ={...this.orderParams};
+      productRepurchaseDataForm.currentLimit = this.repurchaseCurrentLimit
+
+      this.updateProductRepurchaseData(productRepurchaseDataForm)//
     },
 
     // 瀹炰緥鍖栬鍗曟瑙�
@@ -1378,7 +1560,7 @@
 
   mounted() {
     console.log('ECharts 鏄惁瀛樺湪锛�', typeof echarts !== 'undefined'); // 搴旇緭鍑� true
-    // this.initChart();
+    this.initChart();
     this.initBaseParams();
     window.addEventListener('resize', this.handleResize)
   },
@@ -1724,6 +1906,10 @@
   width: 100%;
   height: 662px;
 }
+.repurchase-chart-container{
+  width: 100%;
+  height: 662px;
+}
 .chart-container {
   width: 100%;
   height: 400px;

--
Gitblit v1.8.0