gaoluyang
4 天以前 b3af444b89aa69ab3b012c97a6298cda321c8c19
src/views/salesManagement/strategyControl/index.vue
@@ -474,6 +474,124 @@
          />
        </el-card>
      </el-tab-pane>
      <!-- 指标统计(多维度销售分析) -->
      <el-tab-pane label="指标统计" name="indicatorStats">
        <el-card class="box-card">
          <!-- KPI 汇总 -->
          <el-row :gutter="20" class="stats-row">
            <el-col :span="6">
              <div class="stat-card">
                <div class="stat-icon" style="background: #ecf5ff;">
                  <el-icon :size="30" color="#409eff"><Document /></el-icon>
                </div>
                <div class="stat-content">
                  <div class="stat-value">{{ indicatorKpis.orderCount.toLocaleString() }}</div>
                  <div class="stat-label">订单数量</div>
                </div>
              </div>
            </el-col>
            <el-col :span="6">
              <div class="stat-card">
                <div class="stat-icon" style="background: #f0f9ff;">
                  <el-icon :size="30" color="#67c23a"><Tickets /></el-icon>
                </div>
                <div class="stat-content">
                  <div class="stat-value">¥{{ indicatorKpis.salesAmount.toLocaleString() }}</div>
                  <div class="stat-label">销售额</div>
                </div>
              </div>
            </el-col>
            <el-col :span="6">
              <div class="stat-card">
                <div class="stat-icon" style="background: #fef0f0;">
                  <el-icon :size="30" color="#e6a23c"><Van /></el-icon>
                </div>
                <div class="stat-content">
                  <div class="stat-value">{{ indicatorKpis.shipmentRate }}%</div>
                  <div class="stat-label">发货率</div>
                </div>
              </div>
            </el-col>
            <el-col :span="6">
              <div class="stat-card">
                <div class="stat-icon" style="background: #f4f4f5;">
                  <el-icon :size="30" color="#f56c6c"><Wallet /></el-icon>
                </div>
                <div class="stat-content">
                  <div class="stat-value">{{ indicatorKpis.collectionRate }}%</div>
                  <div class="stat-label">回款率</div>
                </div>
              </div>
            </el-col>
          </el-row>
          <!-- 维度筛选 -->
          <el-row :gutter="20" class="search-row">
            <el-col :span="6">
              <el-select v-model="indicatorFilter.product" placeholder="产品" clearable>
                <el-option label="全部产品" value="" />
                <el-option label="P.O 42.5普通硅酸盐水泥" value="P.O 42.5普通硅酸盐水泥" />
                <el-option label="P.S 32.5矿渣硅酸盐水泥" value="P.S 32.5矿渣硅酸盐水泥" />
                <el-option label="P.C 32.5复合硅酸盐水泥" value="P.C 32.5复合硅酸盐水泥" />
              </el-select>
            </el-col>
            <el-col :span="6">
              <el-select v-model="indicatorFilter.customer" placeholder="客户" clearable>
                <el-option label="全部客户" value="" />
                <el-option label="华东建材集团" value="华东建材集团" />
                <el-option label="长江混凝土公司" value="长江混凝土公司" />
                <el-option label="浦江水泥制品厂" value="浦江水泥制品厂" />
              </el-select>
            </el-col>
            <el-col :span="6">
              <el-select v-model="indicatorFilter.region" placeholder="区域" clearable>
                <el-option label="全部区域" value="" />
                <el-option label="华东地区" value="华东地区" />
                <el-option label="华南地区" value="华南地区" />
                <el-option label="华北地区" value="华北地区" />
              </el-select>
            </el-col>
            <el-col :span="6">
              <el-date-picker v-model="indicatorFilter.dateRange" type="daterange" range-separator="至"
                              start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" style="width: 100%" />
            </el-col>
            <el-col :span="24" style="text-align: right; margin-top: 10px;">
              <el-button type="primary" @click="applyIndicatorFilter">查询</el-button>
              <el-button @click="resetIndicatorFilter">重置</el-button>
              <el-button @click="exportIndicatorTable">导出报表</el-button>
              <el-button @click="exportIndicatorChart">导出图表</el-button>
            </el-col>
          </el-row>
          <!-- 图表区 -->
          <div class="chart-container">
            <div ref="indicatorChartRef" style="width: 100%; height: 360px;"></div>
          </div>
          <!-- 业绩统计(团队维度,无个人姓名) -->
          <el-table :data="teamPerformanceList" border stripe style="margin-top: 20px;">
            <el-table-column prop="team" label="销售团队" width="140" />
            <el-table-column prop="orderCount" label="订单数" width="100" />
            <el-table-column prop="salesAmount" label="销售额" width="140">
              <template #default="scope">¥{{ scope.row.salesAmount.toLocaleString() }}</template>
            </el-table-column>
            <el-table-column prop="shipmentRate" label="发货率" width="100">
              <template #default="scope">{{ scope.row.shipmentRate }}%</template>
            </el-table-column>
            <el-table-column prop="collectionRate" label="回款率" width="100">
              <template #default="scope">{{ scope.row.collectionRate }}%</template>
            </el-table-column>
            <el-table-column prop="attainment" label="目标达成率" width="120">
              <template #default="scope">
                <el-tag :type="scope.row.attainment >= 100 ? 'success' : scope.row.attainment >= 80 ? 'warning' : 'danger'">
                  {{ scope.row.attainment }}%
                </el-tag>
              </template>
            </el-table-column>
          </el-table>
        </el-card>
      </el-tab-pane>
    </el-tabs>
    <!-- 价格策略对话框 -->
@@ -482,7 +600,7 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="策略类型" prop="strategyType">
              <el-select v-model="priceStrategyForm.strategyType" placeholder="请选择策略类型" style="width: 100%;">
              <el-select v-model="priceStrategyForm.strategyType" placeholder="请选择策略类型" style="width: 100%;" :disabled="priceStrategyDialogMode === 'view'">
                <el-option label="专属价格" value="专属价格"></el-option>
                <el-option label="阶梯报价" value="阶梯报价"></el-option>
                <el-option label="促销折扣" value="促销折扣"></el-option>
@@ -491,7 +609,7 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户名称" prop="customerName">
              <el-select v-model="priceStrategyForm.customerName" placeholder="请选择客户" style="width: 100%;">
              <el-select v-model="priceStrategyForm.customerName" placeholder="请选择客户" style="width: 100%;" :disabled="priceStrategyDialogMode === 'view'">
                <el-option label="华东建材集团" value="华东建材集团"></el-option>
                <el-option label="长江混凝土公司" value="长江混凝土公司"></el-option>
                <el-option label="浦江水泥制品厂" value="浦江水泥制品厂"></el-option>
@@ -502,7 +620,7 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="产品名称" prop="productName">
              <el-select v-model="priceStrategyForm.productName" placeholder="请选择产品" style="width: 100%;">
              <el-select v-model="priceStrategyForm.productName" placeholder="请选择产品" style="width: 100%;" :disabled="priceStrategyDialogMode === 'view'">
                <el-option label="P.O 42.5普通硅酸盐水泥" value="P.O 42.5普通硅酸盐水泥"></el-option>
                <el-option label="P.S 32.5矿渣硅酸盐水泥" value="P.S 32.5矿渣硅酸盐水泥"></el-option>
                <el-option label="P.C 32.5复合硅酸盐水泥" value="P.C 32.5复合硅酸盐水泥"></el-option>
@@ -511,19 +629,19 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="规格型号" prop="specification">
              <el-input v-model="priceStrategyForm.specification" placeholder="请输入规格型号" />
              <el-input v-model="priceStrategyForm.specification" placeholder="请输入规格型号" :disabled="priceStrategyDialogMode === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="基础价格(元/吨)" prop="basePrice">
              <el-input-number v-model="priceStrategyForm.basePrice" :min="0" :precision="2" style="width: 100%;" />
              <el-input-number v-model="priceStrategyForm.basePrice" :min="0" :precision="2" style="width: 100%;" :disabled="priceStrategyDialogMode === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="策略价格" prop="strategyPrice">
              <el-input v-model="priceStrategyForm.strategyPrice" placeholder="如: ¥350/吨 或 9折" />
              <el-input v-model="priceStrategyForm.strategyPrice" placeholder="如: ¥350/吨 或 9折" :disabled="priceStrategyDialogMode === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
@@ -536,6 +654,7 @@
                placeholder="选择生效日期"
                style="width: 100%"
                value-format="YYYY-MM-DD"
                :disabled="priceStrategyDialogMode === 'view'"
              />
            </el-form-item>
          </el-col>
@@ -547,17 +666,18 @@
                placeholder="选择失效日期"
                style="width: 100%"
                value-format="YYYY-MM-DD"
                :disabled="priceStrategyDialogMode === 'view'"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="策略说明" prop="description">
          <el-input type="textarea" v-model="priceStrategyForm.description" :rows="3" placeholder="请输入策略说明" />
          <el-input type="textarea" v-model="priceStrategyForm.description" :rows="3" placeholder="请输入策略说明" :disabled="priceStrategyDialogMode === 'view'" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="priceStrategyDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleSavePriceStrategy">保存</el-button>
        <el-button @click="priceStrategyDialogVisible = false">{{ priceStrategyDialogMode === 'view' ? '关闭' : '取消' }}</el-button>
        <el-button v-if="priceStrategyDialogMode !== 'view'" type="primary" @click="handleSavePriceStrategy">保存</el-button>
      </template>
    </el-dialog>
  </div>
@@ -648,6 +768,7 @@
const priceStrategyDialogVisible = ref(false)
const priceStrategyDialogTitle = ref('新增价格策略')
const priceStrategyDialogMode = ref('add') // add | edit | view
const priceStrategyForm = reactive({
  strategyType: '',
  customerName: '',
@@ -849,16 +970,21 @@
const handleAddPriceStrategy = () => {
  priceStrategyDialogTitle.value = '新增价格策略'
  resetPriceStrategyForm()
  priceStrategyDialogMode.value = 'add'
  priceStrategyDialogVisible.value = true
}
const handleViewPriceStrategy = (row) => {
  ElMessage.info('查看策略详情: ' + row.strategyNo)
  priceStrategyDialogTitle.value = '查看价格策略'
  Object.assign(priceStrategyForm, row)
  priceStrategyDialogMode.value = 'view'
  priceStrategyDialogVisible.value = true
}
const handleEditPriceStrategy = (row) => {
  priceStrategyDialogTitle.value = '编辑价格策略'
  Object.assign(priceStrategyForm, row)
  priceStrategyDialogMode.value = 'edit'
  priceStrategyDialogVisible.value = true
}
@@ -868,6 +994,12 @@
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    // 本地假数据删除:从列表中移除并更新总数
    const index = priceStrategyList.value.findIndex(item => item.id === row.id)
    if (index > -1) {
      priceStrategyList.value.splice(index, 1)
      if (pricePagination.total > 0) pricePagination.total -= 1
    }
    ElMessage.success('删除成功')
  })
}
@@ -1176,6 +1308,112 @@
  profitPagination.pageSize = val.limit
}
// ========== 指标统计(多维度分析) ==========
const indicatorKpis = reactive({
  orderCount: 1280,
  salesAmount: 9650000,
  shipmentRate: 89.2,
  collectionRate: 76.4
})
const indicatorFilter = reactive({
  product: '',
  customer: '',
  region: '',
  dateRange: []
})
const indicatorChartRef = ref(null)
let indicatorChart = null
const teamPerformanceList = ref([
  { team: '华东团队A', orderCount: 320, salesAmount: 2850000, shipmentRate: 90, collectionRate: 80, attainment: 105 },
  { team: '华北团队B', orderCount: 280, salesAmount: 2150000, shipmentRate: 86, collectionRate: 73, attainment: 92 },
  { team: '华南团队C', orderCount: 210, salesAmount: 1850000, shipmentRate: 88, collectionRate: 70, attainment: 78 },
  { team: '西南团队D', orderCount: 180, salesAmount: 1500000, shipmentRate: 83, collectionRate: 68, attainment: 74 }
])
const initIndicatorChart = () => {
  if (!indicatorChartRef.value) return
  if (indicatorChart) indicatorChart.dispose()
  indicatorChart = echarts.init(indicatorChartRef.value)
  const option = {
    title: { text: '多维度销售指标趋势', left: 'center' },
    tooltip: { trigger: 'axis' },
    legend: { data: ['订单数', '销售额', '发货率', '回款率'], top: 30 },
    grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
    xAxis: { type: 'category', data: ['2024-12', '2025-01', '2025-02', '2025-03', '2025-04', '2025-05'] },
    yAxis: [
      { type: 'value', name: '数量/金额', axisLabel: { formatter: '{value}' } },
      { type: 'value', name: '比例(%)', min: 0, max: 100, axisLabel: { formatter: '{value}%' } }
    ],
    series: [
      { name: '订单数', type: 'bar', data: [180, 220, 210, 260, 205, 225], itemStyle: { color: '#409eff' } },
      { name: '销售额', type: 'bar', data: [820, 950, 910, 1080, 980, 1020], itemStyle: { color: '#67c23a' } },
      { name: '发货率', type: 'line', yAxisIndex: 1, data: [86, 89, 88, 91, 87, 90], itemStyle: { color: '#e6a23c' } },
      { name: '回款率', type: 'line', yAxisIndex: 1, data: [72, 76, 74, 79, 75, 78], itemStyle: { color: '#f56c6c' } }
    ]
  }
  indicatorChart.setOption(option)
}
const applyIndicatorFilter = () => {
  // 使用假数据模拟查询,刷新KPI和图表
  // 仅演示:随机微调以体现筛选效果
  const random = (base, delta) => {
    const v = base + Math.round((Math.random() - 0.5) * delta)
    return v < 0 ? 0 : v
  }
  indicatorKpis.orderCount = random(1280, 120)
  indicatorKpis.salesAmount = random(9650000, 350000)
  indicatorKpis.shipmentRate = (85 + Math.random() * 10).toFixed(1) * 1
  indicatorKpis.collectionRate = (70 + Math.random() * 12).toFixed(1) * 1
  setTimeout(() => initIndicatorChart(), 200)
}
const resetIndicatorFilter = () => {
  indicatorFilter.product = ''
  indicatorFilter.customer = ''
  indicatorFilter.region = ''
  indicatorFilter.dateRange = []
  applyIndicatorFilter()
}
const exportIndicatorTable = () => {
  // 导出团队业绩为CSV(假导出)
  const header = ['销售团队', '订单数', '销售额', '发货率(%)', '回款率(%)', '目标达成率(%)']
  const rows = teamPerformanceList.value.map(r => [
    r.team,
    r.orderCount,
    r.salesAmount,
    r.shipmentRate,
    r.collectionRate,
    r.attainment
  ])
  const csv = [header, ...rows].map(r => r.join(',')).join('\n')
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = '指标统计-团队业绩.csv'
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
  URL.revokeObjectURL(url)
}
const exportIndicatorChart = () => {
  if (!indicatorChart) return
  const url = indicatorChart.getDataURL({ type: 'png', pixelRatio: 2, backgroundColor: '#fff' })
  const link = document.createElement('a')
  link.href = url
  link.download = '指标统计-图表.png'
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
}
// 生命周期
onMounted(() => {
  // 组件挂载后不立即初始化图表,等待用户切换到对应标签页
@@ -1188,6 +1426,8 @@
      initPriceChart()
    } else if (newVal === 'profitAnalysis') {
      initProfitChart()
    } else if (newVal === 'indicatorStats') {
      initIndicatorChart()
    }
  })
})