From af4f913751c08fd6ef70cb183de2fb3c604bab38 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 31 十月 2025 16:33:15 +0800
Subject: [PATCH] 人力资源-添加导出功能
---
 src/views/salesManagement/strategyControl/index.vue |  260 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 250 insertions(+), 10 deletions(-)
diff --git a/src/views/salesManagement/strategyControl/index.vue b/src/views/salesManagement/strategyControl/index.vue
index 4f5f90d..629d255 100644
--- a/src/views/salesManagement/strategyControl/index.vue
+++ b/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 = () => {
+  // 瀵煎嚭鍥㈤槦涓氱哗涓篊SV锛堝亣瀵煎嚭锛�
+  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()
     }
   })
 })
--
Gitblit v1.9.3