From ea2fb93a0dfcde8f5b66825b20f9d9b835a28acc Mon Sep 17 00:00:00 2001
From: MrShi <1878285526@qq.com>
Date: 星期五, 22 五月 2026 10:54:09 +0800
Subject: [PATCH] 提交

---
 admin/src/views/index.vue | 1309 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 1,059 insertions(+), 250 deletions(-)

diff --git a/admin/src/views/index.vue b/admin/src/views/index.vue
index 20cab96..f1c26ab 100644
--- a/admin/src/views/index.vue
+++ b/admin/src/views/index.vue
@@ -1,278 +1,1087 @@
 <template>
-  <div class="main_home">
-    <div class="home_header">
-      <div class="mb10 fs17">涓嬪崍濂斤紝{{ userInfo.realname }}</div>
-      <div class="fs13">
-        浠婂ぉ鏄� {{ nowDate }} {{ nowWeek }}锛屾杩庤闂珮閾佺珯琛屾潕瀵勫瓨绠$悊绯荤粺
+  <div class="data-board">
+    <div class="header">
+      <h1 class="title">缁忚惀鏁版嵁鐪嬫澘</h1>
+      <div class="filter">
+        <el-button-group>
+          <el-button :type="currentDateType === 0 ? 'primary' : 'default'" @click="onDateTypeChange(0)">浠婃棩</el-button>
+          <el-button :type="currentDateType === 1 ? 'primary' : 'default'" @click="onDateTypeChange(1)">杩�7鏃�</el-button>
+          <el-button :type="currentDateType === 2 ? 'primary' : 'default'" @click="onDateTypeChange(2)">杩�30鏃�</el-button>
+          <el-button :type="currentDateType === 3 ? 'primary' : 'default'" @click="onDateTypeChange(3)">鍗婂勾</el-button>
+          <el-button :type="currentDateType === 4 ? 'primary' : 'default'" @click="onDateTypeChange(4)">涓�骞�</el-button>
+        </el-button-group>
+        <el-select v-model="currentShopId" filterable placeholder="璇烽�夋嫨瀵勫瓨鐐�" style="width: 200px; margin: 0 10px;">
+          <el-option
+            v-for="shop in shopList"
+            :key="shop.id"
+            :label="shop.name"
+            :value="shop.id"
+          ></el-option>
+        </el-select>
+        <el-button type="primary" @click="onSearch">鏌ヨ</el-button>
+        <el-button @click="onReset">閲嶇疆</el-button>
+      </div>
+    </div>
+
+    <div class="stats-row">
+      <div class="stat-card">
+        <div class="stat-label">鍏ラ┗闂ㄥ簵鎬绘暟</div>
+        <div class="stat-value" v-if="overviewData">{{ overviewData.shopCount || 0 }}</div>
+        <div class="stat-value" v-else>-</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">闂ㄥ簵鍏ラ┗鐜�</div>
+        <div class="stat-value" v-if="overviewData">{{ overviewData.shopSettlementRate || 0 }}%</div>
+        <div class="stat-value" v-else>-</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">璁よ瘉鍙告満鎬绘暟</div>
+        <div class="stat-value" v-if="overviewData">{{ overviewData.driverCount || 0 }}</div>
+        <div class="stat-value" v-else>-</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">鍙告満閫氳繃鐜�</div>
+        <div class="stat-value" v-if="overviewData">{{ overviewData.driverPassRate || 0 }}%</div>
+        <div class="stat-value" v-else>-</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">绱浼氬憳鎬绘暟</div>
+        <div class="stat-value" v-if="overviewData">{{ overviewData.memberCount || 0 }}</div>
+        <div class="stat-value" v-else>-</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">鍛ㄦ湡鎬昏鍗曟暟</div>
+        <div class="stat-value" v-if="overviewData">{{ overviewData.orderCount || 0 }}</div>
+        <div class="stat-value" v-else>-</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">鍛ㄦ湡钀ユ敹鎬婚噾棰�</div>
+        <div class="stat-value" v-if="overviewData">楼{{ (overviewData.totalRevenue || 0).toFixed(2) }}</div>
+        <div class="stat-value" v-else>-</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">鍛ㄦ湡閫�娆惧崟鏁�</div>
+        <div class="stat-value" v-if="overviewData">{{ overviewData.refundOrderCount || 0 }}</div>
+        <div class="stat-value" v-else>-</div>
+      </div>
+    </div>
+
+    <div class="charts-row">
+      <div class="chart-card">
+          <div class="chart-header">
+            <span class="chart-title">璁㈠崟琛屾潕绫诲瀷鍗犳瘮</span>
+            <el-button type="text" size="mini" @click="exportPieChart">瀵煎嚭</el-button>
+          </div>
+          <div class="chart-content">
+            <div ref="pieChart" class="pie-chart"></div>
+          </div>
+        </div>
+
+      <div class="chart-card">
+          <div class="chart-header">
+            <span class="chart-title">鏂板浼氬憳璧板娍</span>
+            <div class="date-selectors">
+              <el-select v-model="memberYear" placeholder="閫夋嫨骞翠唤" @change="onMemberYearChange">
+                <el-option
+                  v-for="year in memberYearOptions"
+                  :key="year"
+                  :label="year + '骞�'"
+                  :value="year"
+                ></el-option>
+              </el-select>
+              <el-select v-model="memberMonth" placeholder="閫夋嫨鏈堜唤" :disabled="!memberYear" @change="getMemberTrendData" clearable>
+                <el-option
+                  v-for="month in memberMonthOptions"
+                  :key="month"
+                  :label="month + '鏈�'"
+                  :value="month"
+                ></el-option>
+              </el-select>
+            </div>
+          </div>
+          <div class="chart-content">
+            <div ref="memberChart" class="member-chart"></div>
+          </div>
+        </div>
+    </div>
+
+    <div class="charts-row">
+      <div class="chart-card">
+          <div class="chart-header">
+            <span class="chart-title">璁㈠崟閲忚秼鍔�</span>
+            <div class="date-selectors">
+              <el-select v-model="orderYear" placeholder="閫夋嫨骞翠唤" @change="onOrderYearChange">
+                <el-option
+                  v-for="year in orderYearOptions"
+                  :key="year"
+                  :label="year + '骞�'"
+                  :value="year"
+                ></el-option>
+              </el-select>
+              <el-select v-model="orderMonth" placeholder="閫夋嫨鏈堜唤" :disabled="!orderYear" @change="getOrderTrendData" clearable>
+                <el-option
+                  v-for="month in orderMonthOptions"
+                  :key="month"
+                  :label="month + '鏈�'"
+                  :value="month"
+                ></el-option>
+              </el-select>
+            </div>
+          </div>
+          <div class="chart-content">
+            <div ref="orderChart" class="order-chart"></div>
+          </div>
+        </div>
+
+      <div class="chart-card">
+          <div class="chart-header">
+            <span class="chart-title">钀ユ敹澧為暱鏇茬嚎</span>
+            <div class="date-selectors">
+              <el-select v-model="revenueYear" placeholder="閫夋嫨骞翠唤" @change="onRevenueYearChange">
+                <el-option
+                  v-for="year in revenueYearOptions"
+                  :key="year"
+                  :label="year + '骞�'"
+                  :value="year"
+                ></el-option>
+              </el-select>
+              <el-select v-model="revenueMonth" placeholder="閫夋嫨鏈堜唤" :disabled="!revenueYear" @change="getRevenueTrendData" clearable>
+                <el-option
+                  v-for="month in revenueMonthOptions"
+                  :key="month"
+                  :label="month + '鏈�'"
+                  :value="month"
+                ></el-option>
+              </el-select>
+            </div>
+          </div>
+          <div class="chart-content">
+            <div ref="revenueChart" class="revenue-chart"></div>
+          </div>
+        </div>
+    </div>
+
+    <!-- 骞冲彴璐㈠姟鎬昏 -->
+    <div class="chart-card">
+      <div class="chart-header">
+        <span class="chart-title">骞冲彴璐㈠姟鎬昏</span>
+        <div class="filter-group">
+          <el-date-picker type="daterange" range-separator="鑷�" start-placeholder="寮�濮嬫湀浠�" end-placeholder="缁撴潫鏈堜唤" v-model="financeDateRange" value-format="yyyy-MM" style="margin-right: 10px" :picker-options="financePickerOptions" clearable></el-date-picker>
+          <el-button type="primary" size="mini" @click="getFinanceOverviewData">鏌ヨ</el-button>
+              <el-button size="mini" @click="resetFinanceDate">閲嶇疆</el-button>
+              <el-button type="success" size="mini" @click="exportFinanceData">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <div class="chart-content">
+        <el-table :data="financeList" stripe>
+          <el-table-column prop="date" label="骞存湀" min-width="100px"></el-table-column>
+          <el-table-column prop="storageRevenue" label="瀵勫瓨璁㈠崟钀ユ敹" min-width="120px"></el-table-column>
+          <el-table-column prop="deliveryRevenue" label="瀵勯�佽鍗曡惀鏀�" min-width="120px"></el-table-column>
+          <el-table-column prop="totalRevenue" label="骞冲彴鎬昏惀鏀�" min-width="120px"></el-table-column>
+          <el-table-column prop="shopFeeTotal" label="闂ㄥ簵鍒嗘垚鎬婚" min-width="120px"></el-table-column>
+          <el-table-column prop="driverFeeTotal" label="鍙告満鍒嗘垚鎬婚" min-width="120px"></el-table-column>
+          <el-table-column prop="refundAmount" label="閫�娆炬�婚噾棰�" min-width="120px"></el-table-column>
+          <el-table-column prop="netRevenue" label="骞冲彴鍑�钀ユ敹" min-width="120px"></el-table-column>
+        </el-table>
+      </div>
+    </div>
+
+    <div class="header" style="margin-top: 20px;">
+      <h1 class="title">涓氱哗鎺掑悕鍒嗘瀽</h1>
+      <div class="filter">
+        <el-select v-model="shopTopYear" placeholder="閫夋嫨骞翠唤" style="width: 120px; margin-right: 10px;" @change="onShopTopYearChange">
+          <el-option
+            v-for="year in shopTopYearOptions"
+            :key="year"
+            :label="year + '骞�'"
+            :value="year"
+          ></el-option>
+        </el-select>
+        <el-select v-model="shopTopMonth" placeholder="閫夋嫨鏈堜唤" style="width: 120px;" :disabled="!shopTopYear" @change="onShopTopMonthChange" clearable>
+          <el-option
+            v-for="month in shopTopMonthOptions"
+            :key="month"
+            :label="month + '鏈�'"
+            :value="month"
+          ></el-option>
+        </el-select>
+      </div>
+    </div>
+
+    <!-- 涓氱哗鎺掑悕鍒嗘瀽 -->
+    <div class="charts-row" style="margin-top: 20px; margin-bottom: 0;">
+      <div class="chart-card" style="width: calc(50% - 20px);">
+          <div class="chart-header">
+            <span class="chart-title">闂ㄥ簵涓氱哗TOP10</span>
+          </div>
+        <div class="chart-content">
+          <el-table :data="storeTopList" stripe>
+          <el-table-column type="index" label="鎺掑悕" width="60px"></el-table-column>
+          <el-table-column prop="shopName" label="闂ㄥ簵鍚嶇О" min-width="120px"></el-table-column>
+          <el-table-column prop="completedCount" label="鎬诲畬鎴愯鍗曢噺" min-width="120px"></el-table-column>
+          <el-table-column prop="totalRevenue" label="鎬昏惀鏀堕噾棰�" min-width="120px"></el-table-column>
+          <el-table-column prop="shopFeeTotal" label="闂ㄥ簵鍒嗘垚鎬婚" min-width="120px"></el-table-column>
+          <el-table-column prop="refundCount" label="閫�娆惧崟鏁�" min-width="120px"></el-table-column>
+          <el-table-column prop="penaltyAmount" label="璐d换鎵f鎬婚" min-width="120px"></el-table-column>
+        </el-table>
+        </div>
+      </div>
+
+      <div class="chart-card" style="width: calc(50% - 20px);">
+        <div class="chart-header">
+          <span class="chart-title">鍙告満涓氱哗TOP10</span>
+        </div>
+        <div class="chart-content">
+          <el-table :data="driverTopList" stripe>
+            <el-table-column type="index" label="鎺掑悕" width="60px"></el-table-column>
+            <el-table-column prop="driverName" label="鍙告満濮撳悕" min-width="120px"></el-table-column>
+            <el-table-column prop="completedCount" label="鎬诲畬鎴愯鍗曢噺" min-width="120px"></el-table-column>
+            <el-table-column prop="totalRevenue" label="鎬昏惀鏀堕噾棰�" min-width="120px"></el-table-column>
+            <el-table-column prop="driverFeeTotal" label="鍙告満鍒嗘垚鎬婚" min-width="120px"></el-table-column>
+            <el-table-column prop="refundCount" label="閫�娆惧崟鏁�" min-width="120px"></el-table-column>
+            <el-table-column prop="penaltyAmount" label="璐d换鎵f鎬婚" min-width="120px"></el-table-column>
+          </el-table>
+        </div>
       </div>
     </div>
   </div>
 </template>
 
 <script>
-import dayjs from 'dayjs'
+import { overview, memberTrend, orderTrend, revenueTrend, financeOverview, exportExcel, luggageTypeExport, shopTop, driverTop } from '@/api/business/dataBoard'
+import { fetchList } from '@/api/business/shopInfo'
 import * as echarts from 'echarts'
-import { weeks } from '@/utils/config'
-const colors = ['#52a4f7', '#7678f7', '#5fc6d5']
+
 export default {
-  components: {
-  },
+  name: 'DataBoard',
   data () {
+    const currentYear = new Date().getFullYear()
+    const currentMonth = new Date().getMonth() + 1
+    const startOfYear = `${currentYear}-01`
+    const endOfYear = `${currentYear}-${String(currentMonth).padStart(2, '0')}`
     return {
-      searchForm: {
-        timeType: null,
-        timeName: '鍏ㄩ儴'
+      overviewData: {},
+      currentDateType: 0,
+      currentShopId: null,
+      shopList: [],
+      pieChartInstance: null,
+      currentYear,
+      currentMonth,
+      memberYear: currentYear,
+      memberMonth: null,
+      memberYearOptions: [],
+      memberMonthOptions: [],
+      memberTrendData: [],
+      memberChartInstance: null,
+      orderYear: currentYear,
+      orderMonth: null,
+      orderYearOptions: [],
+      orderMonthOptions: [],
+      orderTrendData: [],
+      orderChartInstance: null,
+      revenueYear: currentYear,
+      revenueMonth: null,
+      revenueYearOptions: [],
+      revenueMonthOptions: [],
+      revenueTrendData: [],
+      revenueChartInstance: null,
+      shopTopYear: currentYear,
+      shopTopMonth: null,
+      shopTopYearOptions: [],
+      shopTopMonthOptions: [],
+      driverTopList: [],
+      financeDateRange: [startOfYear, endOfYear],
+      financePickerOptions: {
+        disabledDate: (time) => {
+          // 绂佺敤鏈潵鏃ユ湡
+          if (time.getTime() > Date.now()) {
+            return true
+          }
+          // 闄愬埗鏃ユ湡鑼冨洿涓嶈秴杩�2骞�
+          if (this.financeMinDate) {
+            const twoYears = 2 * 365 * 24 * 60 * 60 * 1000
+            const maxTime = this.financeMinDate.getTime() + twoYears
+            if (time.getTime() > maxTime) {
+              return true
+            }
+          }
+          return false
+        },
+        onPick: ({ maxDate, minDate }) => {
+          this.financeMinDate = minDate
+          if (maxDate) {
+            this.financeMinDate = null
+          }
+        }
       },
-      colors,
-      nowDate: '',
-      nowWeek: '',
-      headerData: {},
-      headerData1: {},
-      staticData0: {},
-      staticData01: {},
-      staticData1: {},
-      staticData2: {},
-      staticData3: {},
-      staticData4: {},
-      manningRatio: []
-    }
-  },
-  computed: {
-    userInfo () {
-      return this.$store.state.userInfo
+      financeMinDate: null,
+      financeList: [],
+      storeTopList: [],
+      driverTopList: [
+        { driverName: '寮犱紵澶�', phone: '18356981111', totalOrder: '100', totalIncome: '90720.00', driverShare: '90720.00', refundCount: '100', deductionTotal: '90720.00' },
+        { driverName: '鏉庢槑', phone: '18356982222', totalOrder: '80', totalIncome: '32432.00', driverShare: '32432.00', refundCount: '80', deductionTotal: '32432.00' },
+        { driverName: '寮犳澃', phone: '18356981111', totalOrder: '70', totalIncome: '90720.00', driverShare: '90720.00', refundCount: '70', deductionTotal: '90720.00' },
+        { driverName: '瀛欐旦', phone: '18356982222', totalOrder: '70', totalIncome: '90720.00', driverShare: '90720.00', refundCount: '70', deductionTotal: '90720.00' },
+        { driverName: '鏉庢ⅵ', phone: '18356982222', totalOrder: '70', totalIncome: '90720.00', driverShare: '90720.00', refundCount: '70', deductionTotal: '90720.00' },
+        { driverName: '鍒樻璐�', phone: '18356982222', totalOrder: '40', totalIncome: '90720.00', driverShare: '90720.00', refundCount: '40', deductionTotal: '90720.00' },
+        { driverName: '鐜嬫旦鐒�', phone: '18356981111', totalOrder: '40', totalIncome: '90720.00', driverShare: '90720.00', refundCount: '40', deductionTotal: '90720.00' },
+        { driverName: '寮犱紵澶�', phone: '18356982222', totalOrder: '40', totalIncome: '90720.00', driverShare: '90720.00', refundCount: '40', deductionTotal: '90720.00' },
+        { driverName: '鏉庢槑', phone: '18356981111', totalOrder: '40', totalIncome: '90720.00', driverShare: '90720.00', refundCount: '40', deductionTotal: '90720.00' },
+        { driverName: '寮犳澃', phone: '18356982222', totalOrder: '40', totalIncome: '90720.00', driverShare: '90720.00', refundCount: '40', deductionTotal: '90720.00' }
+      ]
     }
   },
   created () {
+    this.getOverviewData()
+    this.getShopList()
+    this.initMemberDateOptions()
+    this.getMemberTrendData()
+    this.initOrderDateOptions()
+    this.getOrderTrendData()
+    this.initRevenueDateOptions()
+    this.getRevenueTrendData()
+    this.initShopTopDateOptions()
+    this.getShopTopData()
+    this.getDriverTopData()
+    this.getFinanceOverviewData()
   },
-  mounted () {
-    this.updateDate()
-    // this.initData()
+  mounted() {
+    this.initPieChart()
+    this.renderPieChart()
+    this.initMemberChart()
+    this.renderMemberChart()
+    this.initOrderChart()
+    this.renderOrderChart()
+    this.initRevenueChart()
+    this.renderRevenueChart()
+    window.addEventListener('resize', this.handleResize)
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize)
+    if (this.pieChartInstance) {
+      this.pieChartInstance.dispose()
+    }
+    if (this.memberChartInstance) {
+      this.memberChartInstance.dispose()
+    }
+    if (this.orderChartInstance) {
+      this.orderChartInstance.dispose()
+    }
+    if (this.revenueChartInstance) {
+      this.revenueChartInstance.dispose()
+    }
   },
   methods: {
-    updateDate () {
-      this.nowDate = dayjs().format('YYYY骞碝鏈圖鏃�')
-      this.nowWeek = weeks[new Date().getDay()]
-    }
+    async getOverviewData(dateType = 0, shopId = null) {
+      const response = await overview({ dateType, shopId })
+      this.overviewData = response || null
+      this.$nextTick(() => {
+        this.renderPieChart()
+      })
+    },
+    onDateTypeChange(dateType) {
+      this.currentDateType = dateType
+      this.getOverviewData(dateType, this.currentShopId)
+    },
+    onSearch() {
+      this.getOverviewData(this.currentDateType, this.currentShopId)
+    },
+    onReset() {
+      this.currentDateType = 0
+      this.currentShopId = null
+      this.getOverviewData(0, null)
+    },
+    async getShopList() {
+      const response = await fetchList({
+        capacity: 99999,
+        page: 1,
+        model: {
+          versionType: 0,
+          auditStatus: 3
+        }
+      })
+      this.shopList = response.records || []
+    },
+    initPieChart() {
+      this.pieChartInstance = echarts.init(this.$refs.pieChart)
+    },
+    renderPieChart() {
+      if (!this.pieChartInstance) {
+        this.initPieChart()
+      }
+      
+      const data = this.overviewData.luggageTypeList || []
+      const chartData = data.map(item => ({
+        value: item.orderCount,
+        name: item.luggageName,
+        luggageCount: item.luggageCount
+      }))
+      
+      const option = {
+        tooltip: {
+          trigger: 'item',
+          formatter: (params) => {
+            return `${params.name}<br/>琛屾潕鏁�: ${params.data.luggageCount}`
+          }
+        },
+        series: [
+          {
+            name: '琛屾潕绫诲瀷',
+            type: 'pie',
+            radius: ['40%', '70%'],
+            avoidLabelOverlap: true,
+            itemStyle: {
+              borderRadius: 10,
+              borderColor: '#fff',
+              borderWidth: 2
+            },
+            label: {
+              show: true,
+              position: 'outside',
+              formatter: '{b}'
+            },
+            emphasis: {
+              label: {
+                show: true,
+                fontSize: 16,
+                fontWeight: 'bold'
+              }
+            },
+            labelLine: {
+              show: true,
+              length: 30,
+              length2: 20,
+              smooth: true
+            },
+            data: chartData
+          }
+        ]
+      }
+      
+      this.pieChartInstance.setOption(option)
+      },
+      handleResize() {
+        if (this.pieChartInstance) {
+          this.pieChartInstance.resize()
+        }
+        if (this.memberChartInstance) {
+          this.memberChartInstance.resize()
+        }
+        if (this.orderChartInstance) {
+          this.orderChartInstance.resize()
+        }
+        if (this.revenueChartInstance) {
+          this.revenueChartInstance.resize()
+        }
+      },
+      async exportPieChart() {
+        try {
+          const dateType = this.currentDateType
+          const shopId = this.currentShopId
+          const params = { dateType, shopId }
+          
+          const response = await luggageTypeExport(params)
+          
+          // 鍒涘缓涓嬭浇閾炬帴
+          const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
+          const url = window.URL.createObjectURL(blob)
+          const link = document.createElement('a')
+          link.href = url
+          
+          // 鐢熸垚鏂囦欢鍚�
+          const dateTypeNames = ['浠婃棩', '杩戜竷澶�', '杩�30澶�', '杩戝崐骞�', '杩戜竴骞�']
+          const shopName = this.shopList.find(shop => shop.id === shopId)?.name || '鍏ㄩ儴瀵勫瓨鐐�'
+          const fileName = `璁㈠崟琛屾潕绫诲瀷鍗犳瘮_${dateTypeNames[dateType]}_${shopName}.xlsx`
+          
+          link.setAttribute('download', fileName)
+          document.body.appendChild(link)
+          link.click()
+          document.body.removeChild(link)
+          window.URL.revokeObjectURL(url)
+          
+          this.$message.success('瀵煎嚭鎴愬姛')
+        } catch (error) {
+          console.error('瀵煎嚭琛屾潕绫诲瀷鏁版嵁澶辫触:', error)
+          this.$message.error('瀵煎嚭澶辫触锛岃绋嶅悗閲嶈瘯')
+        }
+      },
+      initMemberDateOptions() {
+        // 鐢熸垚骞翠唤閫夐」锛屾渶澶氬埌褰撳墠骞�
+        const startYear = this.currentYear - 5
+        this.memberYearOptions = []
+        for (let year = startYear; year <= this.currentYear; year++) {
+          this.memberYearOptions.push(year)
+        }
+        // 鍒濆鍖栨湀浠介�夐」
+        this.updateMemberMonthOptions()
+      },
+      updateMemberMonthOptions() {
+        this.memberMonthOptions = []
+        const maxMonth = this.memberYear === this.currentYear ? this.currentMonth : 12
+        for (let month = 1; month <= maxMonth; month++) {
+          this.memberMonthOptions.push(month)
+        }
+        // 濡傛灉褰撳墠閫夋嫨鐨勬湀浠借秴杩囨渶澶у厑璁稿�硷紝鑷姩璋冩暣
+        if (this.memberMonth > maxMonth) {
+          this.memberMonth = maxMonth
+        }
+      },
+      onMemberYearChange() {
+        this.updateMemberMonthOptions()
+        this.getMemberTrendData()
+      },
+      initOrderDateOptions() {
+        // 鐢熸垚骞翠唤閫夐」锛屾渶澶氬埌褰撳墠骞�
+        const startYear = this.currentYear - 5
+        this.orderYearOptions = []
+        for (let year = startYear; year <= this.currentYear; year++) {
+          this.orderYearOptions.push(year)
+        }
+        // 鍒濆鍖栨湀浠介�夐」
+        this.updateOrderMonthOptions()
+      },
+      updateOrderMonthOptions() {
+        this.orderMonthOptions = []
+        const maxMonth = this.orderYear === this.currentYear ? this.currentMonth : 12
+        for (let month = 1; month <= maxMonth; month++) {
+          this.orderMonthOptions.push(month)
+        }
+        // 濡傛灉褰撳墠閫夋嫨鐨勬湀浠借秴杩囨渶澶у厑璁稿�硷紝鑷姩璋冩暣
+        if (this.orderMonth && this.orderMonth > maxMonth) {
+          this.orderMonth = maxMonth
+        }
+      },
+      onOrderYearChange() {
+        this.updateOrderMonthOptions()
+        this.getOrderTrendData()
+      },
+      async getOrderTrendData() {
+        const params = {}
+        if (this.orderMonth) {
+          params.month = `${this.orderYear}-${String(this.orderMonth).padStart(2, '0')}`
+        } else {
+          params.year = this.orderYear
+        }
+        const response = await orderTrend(params)
+        this.orderTrendData = response || []
+        this.$nextTick(() => {
+          this.renderOrderChart()
+        })
+      },
+      initRevenueDateOptions() {
+        // 鐢熸垚骞翠唤閫夐」锛屾渶澶氬埌褰撳墠骞�
+        const startYear = this.currentYear - 5
+        this.revenueYearOptions = []
+        for (let year = startYear; year <= this.currentYear; year++) {
+          this.revenueYearOptions.push(year)
+        }
+        // 鍒濆鍖栨湀浠介�夐」
+        this.updateRevenueMonthOptions()
+      },
+      updateRevenueMonthOptions() {
+        this.revenueMonthOptions = []
+        const maxMonth = this.revenueYear === this.currentYear ? this.currentMonth : 12
+        for (let month = 1; month <= maxMonth; month++) {
+          this.revenueMonthOptions.push(month)
+        }
+        // 濡傛灉褰撳墠閫夋嫨鐨勬湀浠借秴杩囨渶澶у厑璁稿�硷紝鑷姩璋冩暣
+        if (this.revenueMonth && this.revenueMonth > maxMonth) {
+          this.revenueMonth = maxMonth
+        }
+      },
+      onRevenueYearChange() {
+        this.updateRevenueMonthOptions()
+        this.getRevenueTrendData()
+      },
+      async getRevenueTrendData() {
+        const params = {}
+        if (this.revenueMonth) {
+          params.month = `${this.revenueYear}-${String(this.revenueMonth).padStart(2, '0')}`
+        } else {
+          params.year = this.revenueYear
+        }
+        const response = await revenueTrend(params)
+        this.revenueTrendData = response || []
+        this.$nextTick(() => {
+          this.renderRevenueChart()
+        })
+      },
+      async getFinanceOverviewData() {
+        if (!this.financeDateRange || this.financeDateRange.length !== 2) {
+          // 濡傛灉鏃ユ湡鑼冨洿涓虹┖锛屼娇鐢ㄩ粯璁よ寖鍥达紙浠婂勾1鏈堝埌褰撳墠鏈堬級
+          const currentYear = new Date().getFullYear()
+          const currentMonth = new Date().getMonth() + 1
+          const startOfYear = `${currentYear}-01`
+          const endOfYear = `${currentYear}-${String(currentMonth).padStart(2, '0')}`
+          this.financeDateRange = [startOfYear, endOfYear]
+        }
+        const params = {
+          startDate: this.financeDateRange[0],
+          endDate: this.financeDateRange[1]
+        }
+        const response = await financeOverview(params)
+        this.financeList = response || []
+      },
+      resetFinanceDate() {
+        const currentYear = new Date().getFullYear()
+        const currentMonth = new Date().getMonth() + 1
+        const startOfYear = `${currentYear}-01`
+        const endOfYear = `${currentYear}-${String(currentMonth).padStart(2, '0')}`
+        this.financeDateRange = [startOfYear, endOfYear]
+        this.getFinanceOverviewData()
+      },
+      initShopTopDateOptions() {
+        // 鐢熸垚骞翠唤閫夐」锛屾渶澶氬埌褰撳墠骞�
+        const startYear = this.currentYear - 5
+        this.shopTopYearOptions = []
+        for (let year = startYear; year <= this.currentYear; year++) {
+          this.shopTopYearOptions.push(year)
+        }
+        // 鍒濆鍖栨湀浠介�夐」
+        this.updateShopTopMonthOptions()
+      },
+      updateShopTopMonthOptions() {
+        this.shopTopMonthOptions = []
+        const maxMonth = this.shopTopYear === this.currentYear ? this.currentMonth : 12
+        for (let month = 1; month <= maxMonth; month++) {
+          this.shopTopMonthOptions.push(month)
+        }
+        // 濡傛灉褰撳墠閫夋嫨鐨勬湀浠借秴杩囨渶澶у厑璁稿�硷紝鑷姩璋冩暣
+        if (this.shopTopMonth && this.shopTopMonth > maxMonth) {
+          this.shopTopMonth = maxMonth
+        }
+      },
+      onShopTopYearChange() {
+        this.updateShopTopMonthOptions()
+        // 鍚屾椂鍒锋柊闂ㄥ簵鍜屽徃鏈轰笟缁╂暟鎹�
+        this.getShopTopData()
+        this.getDriverTopData()
+      },
+      onShopTopMonthChange() {
+        // 鍚屾椂鍒锋柊闂ㄥ簵鍜屽徃鏈轰笟缁╂暟鎹�
+        this.getShopTopData()
+        this.getDriverTopData()
+      },
+      async exportFinanceData() {
+        try {
+          // 纭繚鏈夋湁鏁堢殑鏃ユ湡鑼冨洿
+          let startDate, endDate
+          if (!this.financeDateRange || this.financeDateRange.length !== 2) {
+            const currentYear = new Date().getFullYear()
+            const currentMonth = new Date().getMonth() + 1
+            startDate = `${currentYear}-01`
+            endDate = `${currentYear}-${String(currentMonth).padStart(2, '0')}`
+          } else {
+            startDate = this.financeDateRange[0]
+            endDate = this.financeDateRange[1]
+          }
+          
+          const params = {
+            startDate,
+            endDate,
+            type: 'finance' // 瀵煎嚭绫诲瀷鏍囪瘑
+          }
+          
+          const response = await exportExcel(params)
+          
+          // 鍒涘缓涓嬭浇閾炬帴
+          const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
+          const url = window.URL.createObjectURL(blob)
+          const link = document.createElement('a')
+          link.href = url
+          link.setAttribute('download', `璐㈠姟鎬昏_${startDate}_鑷砡${endDate}.xlsx`)
+          document.body.appendChild(link)
+          link.click()
+          document.body.removeChild(link)
+          window.URL.revokeObjectURL(url)
+          
+          this.$message.success('瀵煎嚭鎴愬姛')
+        } catch (error) {
+          console.error('瀵煎嚭璐㈠姟鏁版嵁澶辫触:', error)
+          this.$message.error('瀵煎嚭澶辫触锛岃绋嶅悗閲嶈瘯')
+        }
+      },
+      async getShopTopData() {
+        try {
+          const params = {}
+          if (this.shopTopMonth) {
+            params.month = `${this.shopTopYear}-${String(this.shopTopMonth).padStart(2, '0')}`
+          } else {
+            params.year = this.shopTopYear
+          }
+          const response = await shopTop(params)
+          this.storeTopList = response || []
+        } catch (error) {
+          console.error('鑾峰彇闂ㄥ簵涓氱哗TOP10鏁版嵁澶辫触:', error)
+        }
+      },
+      async getDriverTopData() {
+        try {
+          const params = {}
+          if (this.shopTopMonth) {
+            params.month = `${this.shopTopYear}-${String(this.shopTopMonth).padStart(2, '0')}`
+          } else {
+            params.year = this.shopTopYear
+          }
+          const response = await driverTop(params)
+          this.driverTopList = response || []
+        } catch (error) {
+          console.error('鑾峰彇鍙告満涓氱哗TOP10鏁版嵁澶辫触:', error)
+        }
+      },
+      async getMemberTrendData() {
+        const params = {}
+        if (this.memberMonth) {
+          params.month = `${this.memberYear}-${String(this.memberMonth).padStart(2, '0')}`
+        } else {
+          params.year = this.memberYear
+        }
+        const response = await memberTrend(params)
+        console.log('浼氬憳璧板娍鏁版嵁:', response)
+        this.memberTrendData = response || []
+        this.$nextTick(() => {
+          this.renderMemberChart()
+        })
+      },
+      initMemberChart() {
+        this.memberChartInstance = echarts.init(this.$refs.memberChart)
+      },
+      renderMemberChart() {
+        if (!this.memberChartInstance) {
+          this.initMemberChart()
+        }
+        
+        const xAxisData = this.memberTrendData.map(item => item.date || item.name)
+        const seriesData = this.memberTrendData.map(item => item.count)
+        
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+              type: 'shadow'
+            }
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+          },
+          xAxis: [
+            {
+              type: 'category',
+              data: xAxisData,
+              axisTick: {
+                alignWithLabel: true
+              }
+            }
+          ],
+          yAxis: [
+            {
+              type: 'value',
+              name: '鏂板浼氬憳鏁�'
+            }
+          ],
+          series: [
+            {
+              name: '鏂板浼氬憳',
+              type: 'bar',
+              barWidth: '60%',
+              data: seriesData
+            }
+          ]
+        }
+        
+        this.memberChartInstance.setOption(option)
+      },
+      initOrderChart() {
+        this.orderChartInstance = echarts.init(this.$refs.orderChart)
+      },
+      renderOrderChart() {
+        if (!this.orderChartInstance) {
+          this.initOrderChart()
+        }
+        
+        const xAxisData = this.orderTrendData.map(item => item.date)
+        const localData = this.orderTrendData.map(item => item.localCount)
+        const remoteData = this.orderTrendData.map(item => item.remoteCount)
+        
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+              type: 'cross'
+            },
+            formatter: (params) => {
+              let result = params[0].name + '<br/>'
+              params.forEach(param => {
+                result += param.marker + param.seriesName + ': ' + Math.round(param.value) + '<br/>'
+              })
+              return result
+            }
+          },
+          legend: {
+            data: ['灏卞湴瀵勫瓨', '鍚屽煄瀵勯��']
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+          },
+          xAxis: [
+            {
+              type: 'category',
+              boundaryGap: false,
+              data: xAxisData
+            }
+          ],
+          yAxis: [
+            {
+              type: 'value',
+              name: '璁㈠崟鏁伴噺',
+              axisLabel: {
+                formatter: (value) => Math.round(value)
+              },
+              axisPointer: {
+                label: {
+                  formatter: (params) => Math.round(params.value)
+                }
+              }
+            }
+          ],
+          series: [
+            {
+              name: '灏卞湴瀵勫瓨',
+              type: 'line',
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 6,
+              data: localData
+            },
+            {
+              name: '鍚屽煄瀵勯��',
+              type: 'line',
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 6,
+              data: remoteData
+            }
+          ]
+        }
+        
+        this.orderChartInstance.setOption(option)
+      },
+      initRevenueChart() {
+        this.revenueChartInstance = echarts.init(this.$refs.revenueChart)
+      },
+      renderRevenueChart() {
+        if (!this.revenueChartInstance) {
+          this.initRevenueChart()
+        }
+        
+        const xAxisData = this.revenueTrendData.map(item => item.date)
+        const localData = this.revenueTrendData.map(item => item.localRevenue)
+        const remoteData = this.revenueTrendData.map(item => item.remoteRevenue)
+        
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+              type: 'cross'
+            },
+            formatter: (params) => {
+              let result = params[0].axisValue + '<br/>'
+              params.forEach(item => {
+                result += `${item.marker}${item.seriesName}: 楼${item.value.toFixed(2)}<br/>`
+              })
+              return result
+            }
+          },
+          legend: {
+            data: ['灏卞湴瀵勫瓨', '鍚屽煄瀵勯��']
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+          },
+          xAxis: [
+            {
+              type: 'category',
+              boundaryGap: false,
+              data: xAxisData
+            }
+          ],
+          yAxis: [
+            {
+              type: 'value',
+              name: '钀ユ敹閲戦(鍏�)',
+              axisLabel: {
+                formatter: '楼{value}'
+              }
+            }
+          ],
+          series: [
+            {
+              name: '灏卞湴瀵勫瓨',
+              type: 'line',
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 6,
+              data: localData
+            },
+            {
+              name: '鍚屽煄瀵勯��',
+              type: 'line',
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 6,
+              data: remoteData
+            }
+          ]
+        }
+        
+        this.revenueChartInstance.setOption(option)
+      }
   }
 }
 </script>
 
-<style lang="scss" scoped>
-
-::v-deep .el-input--small .el-input__inner {
-  height: 30px !important; // 杩欓噷灏辨槸淇敼榛樿楂樺害
-  width: 120px;
-}
-div {
+<style scoped>
+.data-board {
+  padding: 20px;
   box-sizing: border-box;
+  background: #f5f7fa;
+  height: calc(100vh - 60px);
+  overflow-y: scroll;
 }
-
-.home_title {
-  font-weight: 600;
-  font-size: 16px;
-  color: #222222;
-  line-height: 22px;
-}
-.mb50{
-  margin-bottom: 50px;
-}
-.main {
+.header {
   display: flex;
-  position: relative;
-  z-index: 99;
-
-  .app_content {
-    flex: 1;
-
-    .static_card {
-      height: 187px;
-      color: #fff;
-      display: flex;
-      justify-content: space-between;
-
-      .card {
-        flex: 1;
-        height: 187px;
-        background: linear-gradient(270deg, #29aeff 0%, #207ff7 100%);
-        box-shadow: 0px 2px 10px 0px rgba(32, 127, 247, 0.4);
-        border-radius: 8px;
-        margin-right: 14px;
-
-        &:nth-of-type(2) {
-          background: linear-gradient(270deg, #8383ff 0%, #6b6eff 100%);
-        }
-
-        &:nth-of-type(3) {
-          background: linear-gradient(270deg, #42d49d 0%, #12bb8b 100%);
-        }
-
-        &:nth-of-type(4) {
-          margin-right: 0;
-          background: linear-gradient(270deg, #c430dee3 0%, #cd04b9cf 100%);
-        //linear-gradient(270deg, #de3049b8 0%, #cd0421d4 100%);
-          //background: linear-gradient(270deg, #30d3de 0%, #04b7cd 100%);
-        }
-
-        .header {
-          height: 103px;
-          display: flex;
-          justify-content: space-between;
-          align-items: center;
-          padding: 20px 20px 12px;
-          border-bottom: 1px solid rgba(255, 255, 255, 0.2);
-
-          img {
-            width: 40px;
-            height: 40px;
-          }
-
-          .num {
-            font-weight: 600;
-            font-size: 30px;
-            margin-top: 12px;
-          }
-        }
-
-        .content {
-          height: 82px;
-          display: flex;
-          flex-direction: column;
-          justify-content: space-between;
-          font-size: 13px;
-          padding: 15px 20px 20px;
-        }
-      }
-    }
-
-    .funcs {
-      height: 149px;
-      padding: 20px;
-      background: #fff;
-      margin: 10px 0;
-
-      .list {
-        display: flex;
-        padding-top: 20px;
-
-        .item {
-          display: flex;
-          flex-direction: column;
-          align-items: center;
-          justify-content: center;
-          margin-right: 24px;
-          cursor: pointer;
-
-          img {
-            width: 44px;
-            height: 44px;
-          }
-        }
-      }
-    }
-  }
-  .static_wrap {
-    .wrap {
-      background: #fff;
-      padding: 20px 20px 10px;
-      border-radius: 2px;
-      border: 1px solid #eeeeee;
-      height: 280px;
-      flex: 1;
-      margin-top: 10px;
-      margin-bottom: 20px;
-      &:nth-of-type(2n) {
-        //margin-left: 10px;
-      }
-      .echart {
-        width: 100%;
-        height: 190px;
-        position: relative;
-        display: flex;
-        div{
-          flex: 1;
-          height: 190px;
-        }
-      }
-
-      .header {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        margin-bottom: 30px;
-
-        .more {
-          font-size: 13px;
-          color: #999999;
-        }
-      }
-    }
-
-    .static1 {
-      .content {
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        height: 100%;
-
-        .echart_wrap {
-          position: relative;
-
-          .pie_text {
-            position: absolute;
-            left: 50%;
-            top: 50%;
-            transform: translate(-50%, -50%);
-            z-index: 999;
-          }
-        }
-
-        .echart {
-          width: 150px;
-          height: 150px;
-        }
-
-        .list {
-          margin-left: 36px;
-
-          .item {
-            display: flex;
-            align-items: center;
-            margin: 8px 0;
-
-            .icon {
-              width: 16px;
-              height: 16px;
-              border-radius: 50%;
-              margin-right: 6px;
-              background: linear-gradient(270deg, #29aeff 0%, #207ff7 100%);
-            }
-
-            .text {
-              margin-right: 6px;
-            }
-          }
-        }
-      }
-    }
-  }
+  /* justify-content: space-between; */
+  align-items: center;
 }
-
-.main_home {
-  background: #f4f7fc;
-  position: relative;
+.title {
+  font-size: 24px;
+  font-weight: bold;
+  color: #303133;
+}
+.filter {
+  display: flex;
+  align-items: center;
+  margin-left: 30px;
+}
+.stats-row {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  margin-bottom: 20px;
+}
+.stat-card {
+  width: calc(100% / 4 - 15px);
+  background: white;
+  padding: 20px;
+  box-sizing: border-box;
+  border-radius: 8px;
+  margin-top: 20px;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+.stat-label {
+  font-size: 14px;
+  color: #909399;
+  margin-bottom: 10px;
+}
+.stat-value {
+  font-size: 32px;
+  font-weight: bold;
+  color: #303133;
+}
+.charts-row {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 20px;
+}
+.chart-card {
+  flex: 1;
+  background: white;
+  padding: 20px;
+  box-sizing: border-box;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+.chart-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.chart-title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #303133;
+}
+.chart-tags {
+  display: flex;
+  gap: 10px;
+}
+.chart-content {
+  /* height: 300px; */
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.chart-placeholder {
   width: 100%;
-  height: 100%;
-  overflow: auto;
-  padding: 92px 20px 20px;
-  .home_header {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 200px;
-    padding: 20px;
-    color: #fff;
-    background: linear-gradient(180deg, #076ae5 0%, rgba(32, 127, 247, 0) 100%);
-  }
+  text-align: center;
 }
-</style>
+
+.pie-chart {
+  width: 100%;
+  height: 300px;
+}
+
+.date-selectors {
+  display: flex;
+  gap: 10px;
+}
+
+.date-selectors .el-select {
+  width: 100px;
+}
+
+.member-chart {
+  width: 100%;
+  height: 300px;
+}
+
+.order-chart {
+  width: 100%;
+  height: 300px;
+}
+
+.revenue-chart {
+  width: 100%;
+  height: 300px;
+}
+
+.filter-group {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.chart-content {
+  padding-top: 20px;
+}
+
+.el-table {
+  font-size: 14px;
+}
+
+.el-table th {
+  background-color: #f5f7fa;
+  font-weight: bold;
+}
+
+</style>
\ No newline at end of file

--
Gitblit v1.9.3