From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/views/mall/statistics/product/components/ProductSummary.vue |  304 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 304 insertions(+), 0 deletions(-)

diff --git a/src/views/mall/statistics/product/components/ProductSummary.vue b/src/views/mall/statistics/product/components/ProductSummary.vue
new file mode 100644
index 0000000..0669223
--- /dev/null
+++ b/src/views/mall/statistics/product/components/ProductSummary.vue
@@ -0,0 +1,304 @@
+<template>
+  <el-card shadow="never">
+    <template #header>
+      <!-- 鏍囬 -->
+      <div class="flex flex-row items-center justify-between">
+        <CardTitle title="鍟嗗搧姒傚喌" />
+        <!-- 鏌ヨ鏉′欢 -->
+        <ShortcutDateRangePicker ref="shortcutDateRangePicker" @change="getProductTrendData">
+          <el-button
+            class="ml-4"
+            @click="handleExport"
+            :loading="exportLoading"
+            v-hasPermi="['statistics:product:export']"
+          >
+            <Icon icon="ep:download" class="mr-1" />瀵煎嚭
+          </el-button>
+        </ShortcutDateRangePicker>
+      </div>
+    </template>
+    <!-- 缁熻鍊� -->
+    <el-row :gutter="16">
+      <el-col :xl="4" :md="8" :sm="24">
+        <SummaryCard
+          title="鍟嗗搧娴忚閲�"
+          tooltip="鍦ㄩ�夊畾鏉′欢涓嬶紝鎵�鏈夊晢鍝佽鎯呴〉琚闂殑娆℃暟锛屼竴涓汉鍦ㄧ粺璁℃椂闂村唴璁块棶澶氭璁颁负澶氭"
+          icon="ep:view"
+          icon-color="bg-blue-100"
+          icon-bg-color="text-blue-500"
+          prefix=""
+          :decimals="0"
+          :value="trendSummary?.value?.browseCount || 0"
+          :percent="
+            calculateRelativeRate(
+              trendSummary?.value?.browseCount,
+              trendSummary?.reference?.browseCount
+            )
+          "
+        />
+      </el-col>
+      <el-col :xl="4" :md="8" :sm="24">
+        <SummaryCard
+          title="鍟嗗搧璁垮鏁�"
+          tooltip="鍦ㄩ�夊畾鏉′欢涓嬶紝璁块棶浠讳綍鍟嗗搧璇︽儏椤电殑浜烘暟锛屼竴涓汉鍦ㄧ粺璁℃椂闂磋寖鍥村唴璁块棶澶氭鍙涓轰竴涓�"
+          icon="ep:user-filled"
+          icon-color="bg-purple-100"
+          icon-bg-color="text-purple-500"
+          prefix=""
+          :decimals="0"
+          :value="trendSummary?.value?.browseUserCount || 0"
+          :percent="
+            calculateRelativeRate(
+              trendSummary?.value?.browseUserCount,
+              trendSummary?.reference?.browseUserCount
+            )
+          "
+        />
+      </el-col>
+      <el-col :xl="4" :md="8" :sm="24">
+        <SummaryCard
+          title="鏀粯浠舵暟"
+          tooltip="鍦ㄩ�夊畾鏉′欢涓嬶紝鎴愬姛浠樻璁㈠崟鐨勫晢鍝佷欢鏁颁箣鍜�"
+          icon="fa-solid:money-check-alt"
+          icon-color="bg-yellow-100"
+          icon-bg-color="text-yellow-500"
+          prefix=""
+          :decimals="0"
+          :value="trendSummary?.value?.orderPayCount || 0"
+          :percent="
+            calculateRelativeRate(
+              trendSummary?.value?.orderPayCount,
+              trendSummary?.reference?.orderPayCount
+            )
+          "
+        />
+      </el-col>
+      <el-col :xl="4" :md="8" :sm="24">
+        <SummaryCard
+          title="鏀粯閲戦"
+          tooltip="鍦ㄩ�夊畾鏉′欢涓嬶紝鎴愬姛浠樻璁㈠崟鐨勫晢鍝侀噾棰濅箣鍜�"
+          icon="ep:warning-filled"
+          icon-color="bg-green-100"
+          icon-bg-color="text-green-500"
+          prefix="锟�"
+          :decimals="2"
+          :value="fenToYuan(trendSummary?.value?.orderPayPrice || 0)"
+          :percent="
+            calculateRelativeRate(
+              trendSummary?.value?.orderPayPrice,
+              trendSummary?.reference?.orderPayPrice
+            )
+          "
+        />
+      </el-col>
+      <el-col :xl="4" :md="8" :sm="24">
+        <SummaryCard
+          title="閫�娆句欢鏁�"
+          tooltip="鍦ㄩ�夊畾鏉′欢涓嬶紝鎴愬姛閫�娆剧殑鍟嗗搧浠舵暟涔嬪拰"
+          icon="fa-solid:wallet"
+          icon-color="bg-cyan-100"
+          icon-bg-color="text-cyan-500"
+          prefix=""
+          :decimals="0"
+          :value="trendSummary?.value?.afterSaleCount || 0"
+          :percent="
+            calculateRelativeRate(
+              trendSummary?.value?.afterSaleCount,
+              trendSummary?.reference?.afterSaleCount
+            )
+          "
+        />
+      </el-col>
+      <el-col :xl="4" :md="8" :sm="24">
+        <SummaryCard
+          title="閫�娆鹃噾棰�"
+          tooltip="鍦ㄩ�夊畾鏉′欢涓嬶紝鎴愬姛閫�娆剧殑鍟嗗搧閲戦涔嬪拰"
+          icon="fa-solid:award"
+          icon-color="bg-yellow-100"
+          icon-bg-color="text-yellow-500"
+          prefix="锟�"
+          :decimals="2"
+          :value="fenToYuan(trendSummary?.value?.afterSaleRefundPrice || 0)"
+          :percent="
+            calculateRelativeRate(
+              trendSummary?.value?.afterSaleRefundPrice,
+              trendSummary?.reference?.afterSaleRefundPrice
+            )
+          "
+        />
+      </el-col>
+    </el-row>
+    <!-- 鎶樼嚎鍥� -->
+    <el-skeleton :loading="trendLoading" animated>
+      <Echart :height="500" :options="lineChartOptions" />
+    </el-skeleton>
+  </el-card>
+</template>
+<script lang="ts" setup>
+import { ProductStatisticsApi, ProductStatisticsVO } from '@/api/mall/statistics/product'
+import SummaryCard from '@/components/SummaryCard/index.vue'
+import { EChartsOption } from 'echarts'
+import { DataComparisonRespVO } from '@/api/mall/statistics/common'
+import { calculateRelativeRate, fenToYuan } from '@/utils'
+import download from '@/utils/download'
+import { CardTitle } from '@/components/Card'
+import * as DateUtil from '@/utils/formatTime'
+import dayjs from 'dayjs'
+
+/** 鍟嗗搧姒傚喌 */
+defineOptions({ name: 'ProductSummary' })
+
+const message = useMessage() // 娑堟伅寮圭獥
+
+const trendLoading = ref(true) // 鍟嗗搧鐘舵�佸姞杞戒腑
+const exportLoading = ref(false) // 瀵煎嚭鐨勫姞杞戒腑
+const trendSummary = ref<DataComparisonRespVO<ProductStatisticsVO>>() // 鍟嗗搧鐘跺喌缁熻鏁版嵁
+const shortcutDateRangePicker = ref()
+
+/** 鎶樼嚎鍥鹃厤缃� */
+const lineChartOptions = reactive<EChartsOption>({
+  dataset: {
+    dimensions: ['time', 'browseCount', 'browseUserCount', 'orderPayPrice', 'afterSaleRefundPrice'],
+    source: []
+  },
+  grid: {
+    left: 20,
+    right: 20,
+    bottom: 20,
+    top: 80,
+    containLabel: true
+  },
+  legend: {
+    top: 50
+  },
+  series: [
+    { name: '鍟嗗搧娴忚閲�', type: 'line', smooth: true, itemStyle: { color: '#B37FEB' } },
+    { name: '鍟嗗搧璁垮鏁�', type: 'line', smooth: true, itemStyle: { color: '#FFAB2B' } },
+    { name: '鏀粯閲戦', type: 'bar', smooth: true, yAxisIndex: 1, itemStyle: { color: '#1890FF' } },
+    { name: '閫�娆鹃噾棰�', type: 'bar', smooth: true, yAxisIndex: 1, itemStyle: { color: '#00C050' } }
+  ],
+  toolbox: {
+    feature: {
+      // 鏁版嵁鍖哄煙缂╂斁
+      dataZoom: {
+        yAxisIndex: false // Y杞翠笉缂╂斁
+      },
+      brush: {
+        type: ['lineX', 'clear'] // 鍖哄煙缂╂斁鎸夐挳銆佽繕鍘熸寜閽�
+      },
+      saveAsImage: { show: true, name: '鍟嗗搧鐘跺喌' } // 淇濆瓨涓哄浘鐗�
+    }
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'cross'
+    },
+    padding: [5, 10]
+  },
+  xAxis: {
+    type: 'category',
+    boundaryGap: true,
+    axisTick: {
+      show: false
+    }
+  },
+  yAxis: [
+    {
+      type: 'value',
+      name: '閲戦',
+      axisLine: {
+        show: false
+      },
+      axisTick: {
+        show: false
+      },
+      axisLabel: {
+        textStyle: {
+          color: '#7F8B9C'
+        }
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          color: '#F5F7F9'
+        }
+      }
+    },
+    {
+      type: 'value',
+      name: '鏁伴噺',
+      axisLine: {
+        show: false
+      },
+      axisTick: {
+        show: false
+      },
+      axisLabel: {
+        textStyle: {
+          color: '#7F8B9C'
+        }
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          color: '#F5F7F9'
+        }
+      }
+    }
+  ]
+}) as EChartsOption
+
+/** 澶勭悊鍟嗗搧鐘跺喌鏌ヨ */
+const getProductTrendData = async () => {
+  trendLoading.value = true
+  // 1. 澶勭悊鏃堕棿: 寮�濮嬩笌鎴鍦ㄥ悓涓�澶╃殑, 鎶樼嚎鍥惧嚭涓嶆潵, 闇�瑕佸欢闀夸竴澶�
+  const times = shortcutDateRangePicker.value.times
+  if (DateUtil.isSameDay(times[0], times[1])) {
+    // 鍓嶅ぉ
+    times[0] = DateUtil.formatDate(dayjs(times[0]).subtract(1, 'd'))
+  }
+  // 鏌ヨ鏁版嵁
+  await Promise.all([getProductTrendSummary(), getProductStatisticsList()])
+  trendLoading.value = false
+}
+
+/** 鏌ヨ鍟嗗搧鐘跺喌鏁版嵁缁熻 */
+const getProductTrendSummary = async () => {
+  const times = shortcutDateRangePicker.value.times
+  trendSummary.value = await ProductStatisticsApi.getProductStatisticsAnalyse({ times })
+}
+
+/** 鏌ヨ鍟嗗搧鐘跺喌鏁版嵁鍒楄〃 */
+const getProductStatisticsList = async () => {
+  // 鏌ヨ鏁版嵁
+  const times = shortcutDateRangePicker.value.times
+  const list: ProductStatisticsVO[] = await ProductStatisticsApi.getProductStatisticsList({ times })
+  // 澶勭悊鏁版嵁
+  for (let item of list) {
+    item.orderPayPrice = fenToYuan(item.orderPayPrice)
+    item.afterSaleRefundPrice = fenToYuan(item.afterSaleRefundPrice)
+  }
+  // 鏇存柊 Echarts 鏁版嵁
+  if (lineChartOptions.dataset && lineChartOptions.dataset['source']) {
+    lineChartOptions.dataset['source'] = list
+  }
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = async () => {
+  try {
+    // 瀵煎嚭鐨勪簩娆$‘璁�
+    await message.exportConfirm()
+    // 鍙戣捣瀵煎嚭
+    exportLoading.value = true
+    const times = shortcutDateRangePicker.value.times
+    const data = await ProductStatisticsApi.exportProductStatisticsExcel({ times })
+    download.excel(data, '鍟嗗搧鐘跺喌.xls')
+  } catch {
+  } finally {
+    exportLoading.value = false
+  }
+}
+</script>
+<style lang="scss" scoped></style>

--
Gitblit v1.8.0