MrShi
8 小时以前 ea2fb93a0dfcde8f5b66825b20f9d9b835a28acc
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="责任扣款总额" 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="责任扣款总额" 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
      },
      colors,
      nowDate: '',
      nowWeek: '',
      headerData: {},
      headerData1: {},
      staticData0: {},
      staticData01: {},
      staticData1: {},
      staticData2: {},
      staticData3: {},
      staticData4: {},
      manningRatio: []
        onPick: ({ maxDate, minDate }) => {
          this.financeMinDate = minDate
          if (maxDate) {
            this.financeMinDate = null
          }
    }
  },
  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()
    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年M月D日')
      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 {
  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;
  /* 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;
.title {
  font-size: 24px;
  font-weight: bold;
  color: #303133;
          }
        }
        .content {
          height: 82px;
.filter {
          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;
  margin-left: 30px;
          }
        }
      }
    }
  }
  .static_wrap {
    .wrap {
      background: #fff;
      padding: 20px 20px 10px;
      border-radius: 2px;
      border: 1px solid #eeeeee;
      height: 280px;
      flex: 1;
      margin-top: 10px;
.stats-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: space-between;
      margin-bottom: 20px;
      &:nth-of-type(2n) {
        //margin-left: 10px;
      }
      .echart {
        width: 100%;
        height: 190px;
        position: relative;
.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;
        div{
  gap: 20px;
  margin-bottom: 20px;
}
.chart-card {
          flex: 1;
          height: 190px;
  background: white;
  padding: 20px;
  box-sizing: border-box;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
        }
      }
      .header {
.chart-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 30px;
        .more {
          font-size: 13px;
          color: #999999;
        }
.chart-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
      }
    }
    .static1 {
      .content {
.chart-tags {
        display: flex;
  gap: 10px;
}
.chart-content {
  /* height: 300px; */
  display: flex;
  align-items: center;
        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;
          }
.chart-placeholder {
  width: 100%;
  text-align: center;
        }
        .echart {
          width: 150px;
          height: 150px;
.pie-chart {
  width: 100%;
  height: 300px;
        }
        .list {
          margin-left: 36px;
.date-selectors {
  display: flex;
  gap: 10px;
}
          .item {
.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;
            margin: 8px 0;
            .icon {
              width: 16px;
              height: 16px;
              border-radius: 50%;
              margin-right: 6px;
              background: linear-gradient(270deg, #29aeff 0%, #207ff7 100%);
  gap: 10px;
            }
            .text {
              margin-right: 6px;
            }
          }
        }
      }
    }
  }
.chart-content {
  padding-top: 20px;
}
.main_home {
  background: #f4f7fc;
  position: relative;
  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%);
.el-table {
  font-size: 14px;
  }
.el-table th {
  background-color: #f5f7fa;
  font-weight: bold;
}
</style>