From db42d47f5692ef64e5436c5a6d29dcb537b44596 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期一, 26 一月 2026 16:36:13 +0800
Subject: [PATCH] 浪潮对接单点登录:mis调整
---
src/views/salesManagement/strategyControl/index.vue | 1587 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,587 insertions(+), 0 deletions(-)
diff --git a/src/views/salesManagement/strategyControl/index.vue b/src/views/salesManagement/strategyControl/index.vue
new file mode 100644
index 0000000..629d255
--- /dev/null
+++ b/src/views/salesManagement/strategyControl/index.vue
@@ -0,0 +1,1587 @@
+<template>
+ <div class="app-container strategy-control">
+ <el-tabs v-model="activeTab" type="border-card" class="main-tabs" @tab-change="handleTabChange">
+ <!-- 浠锋牸绛栫暐閰嶇疆 -->
+ <el-tab-pane label="浠锋牸绛栫暐閰嶇疆" name="priceStrategy">
+ <el-card class="box-card">
+ <el-row :gutter="20" class="search-row">
+ <el-col :span="6">
+ <el-select v-model="priceSearchForm.customerName" placeholder="璇烽�夋嫨瀹㈡埛" clearable>
+ <el-option label="鍏ㄩ儴瀹㈡埛" value=""></el-option>
+ <el-option label="鍗庝笢寤烘潗闆嗗洟" value="鍗庝笢寤烘潗闆嗗洟"></el-option>
+ <el-option label="闀挎睙娣峰嚌鍦熷叕鍙�" value="闀挎睙娣峰嚌鍦熷叕鍙�"></el-option>
+ <el-option label="娴︽睙姘存偿鍒跺搧鍘�" value="娴︽睙姘存偿鍒跺搧鍘�"></el-option>
+ </el-select>
+ </el-col>
+ <el-col :span="6">
+ <el-select v-model="priceSearchForm.productType" placeholder="璇烽�夋嫨姘存偿绫诲瀷" clearable>
+ <el-option label="鍏ㄩ儴绫诲瀷" value=""></el-option>
+ <el-option label="鏅�氱閰哥洂姘存偿" value="鏅�氱閰哥洂姘存偿"></el-option>
+ <el-option label="鐭挎福纭呴吀鐩愭按娉�" value="鐭挎福纭呴吀鐩愭按娉�"></el-option>
+ <el-option label="澶嶅悎纭呴吀鐩愭按娉�" value="澶嶅悎纭呴吀鐩愭按娉�"></el-option>
+ </el-select>
+ </el-col>
+ <el-col :span="6">
+ <el-select v-model="priceSearchForm.strategyType" placeholder="绛栫暐绫诲瀷" clearable>
+ <el-option label="鍏ㄩ儴绛栫暐" value=""></el-option>
+ <el-option label="涓撳睘浠锋牸" value="涓撳睘浠锋牸"></el-option>
+ <el-option label="闃舵鎶ヤ环" value="闃舵鎶ヤ环"></el-option>
+ <el-option label="淇冮攢鎶樻墸" value="淇冮攢鎶樻墸"></el-option>
+ </el-select>
+ </el-col>
+ <el-col :span="6">
+ <el-button type="primary" @click="searchPriceStrategy">鏌ヨ</el-button>
+ <el-button @click="resetPriceSearch">閲嶇疆</el-button>
+ <el-button type="primary" @click="handleAddPriceStrategy">鏂板绛栫暐</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table :data="priceStrategyList" border stripe v-loading="priceLoading" height="calc(100vh - 26em)">
+ <el-table-column prop="id" label="ID" width="60" align="center"/>
+ <el-table-column prop="strategyNo" label="绛栫暐缂栧彿" width="150"/>
+ <el-table-column prop="strategyType" label="绛栫暐绫诲瀷" width="100">
+ <template #default="scope">
+ <el-tag :type="getStrategyTypeColor(scope.row.strategyType)">
+ {{ scope.row.strategyType }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="customerName" label="瀹㈡埛鍚嶇О" width="180"/>
+ <el-table-column prop="productName" label="浜у搧鍚嶇О" width="200"/>
+ <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" width="120"/>
+ <el-table-column prop="basePrice" label="鍩虹浠锋牸" width="100">
+ <template #default="scope">
+ 楼{{ scope.row.basePrice }}/鍚�
+ </template>
+ </el-table-column>
+ <el-table-column prop="strategyPrice" label="绛栫暐浠锋牸" width="120">
+ <template #default="scope">
+ <span style="color: #f56c6c; font-weight: bold;">
+ {{ scope.row.strategyPrice }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="validPeriod" label="鏈夋晥鏈�" width="200">
+ <template #default="scope">
+ {{ scope.row.startDate }} 鑷� {{ scope.row.endDate }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="status" label="鐘舵��" width="80">
+ <template #default="scope">
+ <el-tag :type="scope.row.status === '鐢熸晥涓�' ? 'success' : 'info'">
+ {{ scope.row.status }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="200" fixed="right" align="center">
+ <template #default="scope">
+ <el-button link type="primary" @click="handleViewPriceStrategy(scope.row)">鏌ョ湅</el-button>
+ <el-button link type="primary" @click="handleEditPriceStrategy(scope.row)">缂栬緫</el-button>
+ <el-button link type="danger" @click="handleDeletePriceStrategy(scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <pagination
+ :total="pricePagination.total"
+ :page="pricePagination.currentPage"
+ :limit="pricePagination.pageSize"
+ @pagination="handlePricePageChange"
+ />
+ </el-card>
+ </el-tab-pane>
+
+ <!-- 鍚堝悓鎵ц鐩戞帶 -->
+ <el-tab-pane label="鍚堝悓鎵ц鐩戞帶" name="contractMonitor">
+ <el-card class="box-card">
+ <!-- 缁熻姒傝 -->
+ <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">{{ contractStats.totalContracts }}</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"><Van /></el-icon>
+ </div>
+ <div class="stat-content">
+ <div class="stat-value">{{ contractStats.deliveryRate }}%</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"><Tickets /></el-icon>
+ </div>
+ <div class="stat-content">
+ <div class="stat-value">{{ contractStats.invoiceRate }}%</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">{{ contractStats.paymentRate }}%</div>
+ <div class="stat-label">鍥炴瀹屾垚鐜�</div>
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+
+ <!-- 鎼滅储鍖哄煙 -->
+ <el-row :gutter="20" class="search-row">
+ <el-col :span="6">
+ <el-input v-model="contractSearchForm.contractNo" placeholder="璇疯緭鍏ュ悎鍚岀紪鍙�" clearable>
+ <template #prefix>
+ <el-icon><Search /></el-icon>
+ </template>
+ </el-input>
+ </el-col>
+ <el-col :span="6">
+ <el-select v-model="contractSearchForm.customerName" placeholder="璇烽�夋嫨瀹㈡埛" clearable>
+ <el-option label="鍗庝笢寤烘潗闆嗗洟" value="鍗庝笢寤烘潗闆嗗洟"></el-option>
+ <el-option label="闀挎睙娣峰嚌鍦熷叕鍙�" value="闀挎睙娣峰嚌鍦熷叕鍙�"></el-option>
+ <el-option label="娴︽睙姘存偿鍒跺搧鍘�" value="娴︽睙姘存偿鍒跺搧鍘�"></el-option>
+ </el-select>
+ </el-col>
+ <el-col :span="6">
+ <el-select v-model="contractSearchForm.executionStatus" placeholder="鎵ц鐘舵��" clearable>
+ <el-option label="寰呮墽琛�" value="寰呮墽琛�"></el-option>
+ <el-option label="鎵ц涓�" value="鎵ц涓�"></el-option>
+ <el-option label="宸插畬鎴�" value="宸插畬鎴�"></el-option>
+ <el-option label="寮傚父" value="寮傚父"></el-option>
+ </el-select>
+ </el-col>
+ <el-col :span="6">
+ <el-button type="primary" @click="searchContract">鏌ヨ</el-button>
+ <el-button @click="resetContractSearch">閲嶇疆</el-button>
+ </el-col>
+ </el-row>
+
+ <!-- 鍚堝悓鍒楄〃 -->
+ <el-table :data="contractList" border stripe v-loading="contractLoading" height="calc(100vh - 36em)">
+ <el-table-column type="expand">
+ <template #default="scope">
+ <div class="contract-detail-expand">
+ <el-steps :active="getContractStep(scope.row)" align-center>
+ <el-step title="璁㈠崟纭" :description="scope.row.orderDate">
+ <template #icon>
+ <el-icon :color="scope.row.orderStatus === '宸插畬鎴�' ? '#67c23a' : '#909399'">
+ <Check v-if="scope.row.orderStatus === '宸插畬鎴�'" />
+ <Clock v-else />
+ </el-icon>
+ </template>
+ </el-step>
+ <el-step title="璐х墿浜や粯" :description="`${scope.row.deliveryProgress}%`">
+ <template #icon>
+ <el-icon :color="scope.row.deliveryProgress === 100 ? '#67c23a' : '#409eff'">
+ <Check v-if="scope.row.deliveryProgress === 100" />
+ <Van v-else />
+ </el-icon>
+ </template>
+ </el-step>
+ <el-step title="鍙戠エ寮�鍏�" :description="`${scope.row.invoiceProgress}%`">
+ <template #icon>
+ <el-icon :color="scope.row.invoiceProgress === 100 ? '#67c23a' : '#e6a23c'">
+ <Check v-if="scope.row.invoiceProgress === 100" />
+ <Tickets v-else />
+ </el-icon>
+ </template>
+ </el-step>
+ <el-step title="娆鹃」鏀跺洖" :description="`${scope.row.paymentProgress}%`">
+ <template #icon>
+ <el-icon :color="scope.row.paymentProgress === 100 ? '#67c23a' : '#f56c6c'">
+ <Check v-if="scope.row.paymentProgress === 100" />
+ <Wallet v-else />
+ </el-icon>
+ </template>
+ </el-step>
+ </el-steps>
+ </div>
+ </template>
+ </el-table-column>
+ <el-table-column prop="contractNo" label="鍚堝悓缂栧彿" width="150"/>
+ <el-table-column prop="customerName" label="瀹㈡埛鍚嶇О" width="180"/>
+ <el-table-column prop="contractAmount" label="鍚堝悓閲戦" width="120">
+ <template #default="scope">
+ 楼{{ scope.row.contractAmount.toLocaleString() }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="signDate" label="绛捐鏃ユ湡" width="120"/>
+ <el-table-column label="鎵ц杩涘害" width="150">
+ <template #default="scope">
+ <el-progress
+ :percentage="scope.row.executionProgress"
+ :color="getProgressColor(scope.row.executionProgress)"
+ />
+ </template>
+ </el-table-column>
+ <el-table-column prop="deliveryProgress" label="浜や粯杩涘害" width="100">
+ <template #default="scope">
+ {{ scope.row.deliveryProgress }}%
+ </template>
+ </el-table-column>
+ <el-table-column prop="invoiceProgress" label="寮�绁ㄨ繘搴�" width="100">
+ <template #default="scope">
+ {{ scope.row.invoiceProgress }}%
+ </template>
+ </el-table-column>
+ <el-table-column prop="paymentProgress" label="鍥炴杩涘害" width="100">
+ <template #default="scope">
+ {{ scope.row.paymentProgress }}%
+ </template>
+ </el-table-column>
+ <el-table-column prop="executionStatus" label="鎵ц鐘舵��" width="100">
+ <template #default="scope">
+ <el-tag :type="getExecutionStatusType(scope.row.executionStatus)">
+ {{ scope.row.executionStatus }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="120" fixed="right" align="center">
+ <template #default="scope">
+ <el-button link type="primary" @click="handleViewContract(scope.row)">鏌ョ湅璇︽儏</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <pagination
+ :total="contractPagination.total"
+ :page="contractPagination.currentPage"
+ :limit="contractPagination.pageSize"
+ @pagination="handleContractPageChange"
+ />
+ </el-card>
+ </el-tab-pane>
+
+ <!-- 鍘嗗彶姣斾环鍒嗘瀽 -->
+ <el-tab-pane label="鍘嗗彶姣斾环鍒嗘瀽" name="priceComparison">
+ <el-card class="box-card">
+ <el-row :gutter="20" class="search-row">
+ <el-col :span="6">
+ <el-select v-model="compareSearchForm.productName" placeholder="璇烽�夋嫨浜у搧" clearable>
+ <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>
+ </el-select>
+ </el-col>
+ <el-col :span="8">
+ <el-date-picker
+ v-model="compareSearchForm.dateRange"
+ type="daterange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ style="width: 100%"
+ />
+ </el-col>
+ <el-col :span="6">
+ <el-select v-model="compareSearchForm.region" placeholder="閿�鍞尯鍩�" clearable>
+ <el-option label="鍗庝笢鍦板尯" value="鍗庝笢鍦板尯"></el-option>
+ <el-option label="鍗庡崡鍦板尯" value="鍗庡崡鍦板尯"></el-option>
+ <el-option label="鍗庡寳鍦板尯" value="鍗庡寳鍦板尯"></el-option>
+ </el-select>
+ </el-col>
+ <el-col :span="4">
+ <el-button type="primary" @click="searchPriceComparison">鏌ヨ</el-button>
+ <el-button @click="resetCompareSearch">閲嶇疆</el-button>
+ </el-col>
+ </el-row>
+
+ <!-- 浠锋牸瓒嬪娍鍥� -->
+ <div class="chart-container">
+ <div ref="priceChartRef" style="width: 100%; height: 350px;"></div>
+ </div>
+
+ <!-- 鍘嗗彶浠锋牸鍒楄〃 -->
+ <el-table :data="priceComparisonList" border stripe v-loading="compareLoading" style="margin-top: 20px;">
+ <el-table-column prop="date" label="鏃ユ湡" width="120"/>
+ <el-table-column prop="productName" label="浜у搧鍚嶇О" width="200"/>
+ <el-table-column prop="specification" label="瑙勬牸" width="120"/>
+ <el-table-column prop="customerName" label="瀹㈡埛" width="180"/>
+ <el-table-column prop="region" label="鍖哄煙" width="100"/>
+ <el-table-column prop="quantity" label="鏁伴噺(鍚�)" width="100" align="right">
+ <template #default="scope">
+ {{ scope.row.quantity.toLocaleString() }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="price" label="鎴愪氦鍗曚环" width="100">
+ <template #default="scope">
+ 楼{{ scope.row.price }}/鍚�
+ </template>
+ </el-table-column>
+ <el-table-column prop="totalAmount" label="鎴愪氦閲戦" width="120">
+ <template #default="scope">
+ 楼{{ scope.row.totalAmount.toLocaleString() }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="priceChange" label="浠锋牸鍙樺姩" width="100">
+ <template #default="scope">
+ <span :style="{ color: scope.row.priceChange > 0 ? '#f56c6c' : scope.row.priceChange < 0 ? '#67c23a' : '#909399' }">
+ {{ scope.row.priceChange > 0 ? '+' : '' }}{{ scope.row.priceChange }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="remark" label="澶囨敞" show-overflow-tooltip/>
+ </el-table>
+ </el-card>
+ </el-tab-pane>
+
+ <!-- 鍒╂鼎鍒嗘瀽 -->
+ <el-tab-pane label="鍒╂鼎鍒嗘瀽" name="profitAnalysis">
+ <el-card class="box-card">
+ <!-- 鍒╂鼎缁熻鍗$墖 -->
+ <el-row :gutter="20" class="profit-stats-row">
+ <el-col :span="8">
+ <div class="profit-card">
+ <div class="profit-header">鎬婚攢鍞</div>
+ <div class="profit-value">楼{{ profitStats.totalSales.toLocaleString() }}</div>
+ <div class="profit-footer">
+ <span>杈冧笂鏈�</span>
+ <span :class="profitStats.salesGrowth > 0 ? 'growth-up' : 'growth-down'">
+ {{ profitStats.salesGrowth > 0 ? '+' : '' }}{{ profitStats.salesGrowth }}%
+ </span>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="profit-card">
+ <div class="profit-header">鎬绘垚鏈�</div>
+ <div class="profit-value">楼{{ profitStats.totalCost.toLocaleString() }}</div>
+ <div class="profit-footer">
+ <span>鎴愭湰鐜�</span>
+ <span class="cost-rate">{{ profitStats.costRate }}%</span>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="profit-card">
+ <div class="profit-header">姣涘埄娑�</div>
+ <div class="profit-value profit-highlight">楼{{ profitStats.grossProfit.toLocaleString() }}</div>
+ <div class="profit-footer">
+ <span>姣涘埄鐜�</span>
+ <span class="gross-profit-rate">{{ profitStats.grossProfitRate }}%</span>
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+
+ <!-- 鎼滅储鍖哄煙 -->
+ <el-row :gutter="20" class="search-row">
+ <el-col :span="6">
+ <el-select v-model="profitSearchForm.productType" placeholder="浜у搧绫诲瀷" clearable>
+ <el-option label="鏅�氱閰哥洂姘存偿" value="鏅�氱閰哥洂姘存偿"></el-option>
+ <el-option label="鐭挎福纭呴吀鐩愭按娉�" value="鐭挎福纭呴吀鐩愭按娉�"></el-option>
+ <el-option label="澶嶅悎纭呴吀鐩愭按娉�" value="澶嶅悎纭呴吀鐩愭按娉�"></el-option>
+ </el-select>
+ </el-col>
+ <el-col :span="6">
+ <el-select v-model="profitSearchForm.customerName" placeholder="瀹㈡埛鍚嶇О" clearable>
+ <el-option label="鍗庝笢寤烘潗闆嗗洟" value="鍗庝笢寤烘潗闆嗗洟"></el-option>
+ <el-option label="闀挎睙娣峰嚌鍦熷叕鍙�" value="闀挎睙娣峰嚌鍦熷叕鍙�"></el-option>
+ <el-option label="娴︽睙姘存偿鍒跺搧鍘�" value="娴︽睙姘存偿鍒跺搧鍘�"></el-option>
+ </el-select>
+ </el-col>
+ <el-col :span="8">
+ <el-date-picker
+ v-model="profitSearchForm.dateRange"
+ type="monthrange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫湀浠�"
+ end-placeholder="缁撴潫鏈堜唤"
+ value-format="YYYY-MM"
+ style="width: 100%"
+ />
+ </el-col>
+ <el-col :span="4">
+ <el-button type="primary" @click="searchProfit">鏌ヨ</el-button>
+ <el-button @click="resetProfitSearch">閲嶇疆</el-button>
+ </el-col>
+ </el-row>
+
+ <!-- 鍒╂鼎鍒嗘瀽鍥捐〃 -->
+ <div class="chart-container">
+ <div ref="profitChartRef" style="width: 100%; height: 350px;"></div>
+ </div>
+
+ <!-- 鍒╂鼎鏄庣粏琛� -->
+ <el-table :data="profitAnalysisList" border stripe v-loading="profitLoading" style="margin-top: 20px;" show-summary :summary-method="getProfitSummary">
+ <el-table-column prop="orderNo" label="璁㈠崟缂栧彿" width="150"/>
+ <el-table-column prop="customerName" label="瀹㈡埛鍚嶇О" width="180"/>
+ <el-table-column prop="productName" label="浜у搧鍚嶇О" width="200"/>
+ <el-table-column prop="quantity" label="鏁伴噺(鍚�)" width="100" align="right">
+ <template #default="scope">
+ {{ scope.row.quantity.toLocaleString() }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="salesPrice" label="閿�鍞崟浠�" width="100">
+ <template #default="scope">
+ 楼{{ scope.row.salesPrice }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="costPrice" label="鎴愭湰鍗曚环" width="100">
+ <template #default="scope">
+ 楼{{ scope.row.costPrice }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="salesAmount" label="閿�鍞噾棰�" width="120" align="right">
+ <template #default="scope">
+ 楼{{ scope.row.salesAmount.toLocaleString() }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="costAmount" label="鎴愭湰閲戦" width="120" align="right">
+ <template #default="scope">
+ 楼{{ scope.row.costAmount.toLocaleString() }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="grossProfit" label="姣涘埄娑�" width="120" align="right">
+ <template #default="scope">
+ <span :style="{ color: scope.row.grossProfit > 0 ? '#67c23a' : '#f56c6c', fontWeight: 'bold' }">
+ 楼{{ scope.row.grossProfit.toLocaleString() }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="grossProfitRate" label="姣涘埄鐜�" width="100">
+ <template #default="scope">
+ <el-tag :type="getProfitRateType(scope.row.grossProfitRate)">
+ {{ scope.row.grossProfitRate }}%
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="orderDate" label="璁㈠崟鏃ユ湡" width="120"/>
+ </el-table>
+
+ <pagination
+ :total="profitPagination.total"
+ :page="profitPagination.currentPage"
+ :limit="profitPagination.pageSize"
+ @pagination="handleProfitPageChange"
+ />
+ </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>
+
+ <!-- 浠锋牸绛栫暐瀵硅瘽妗� -->
+ <el-dialog v-model="priceStrategyDialogVisible" :title="priceStrategyDialogTitle" width="900px" :close-on-click-modal="false">
+ <el-form :model="priceStrategyForm" :rules="priceStrategyRules" ref="priceStrategyFormRef" label-width="120px">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="绛栫暐绫诲瀷" prop="strategyType">
+ <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>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="瀹㈡埛鍚嶇О" prop="customerName">
+ <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>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="浜у搧鍚嶇О" prop="productName">
+ <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>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="瑙勬牸鍨嬪彿" prop="specification">
+ <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%;" :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鎶�" :disabled="priceStrategyDialogMode === 'view'" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鐢熸晥鏃ユ湡" prop="startDate">
+ <el-date-picker
+ v-model="priceStrategyForm.startDate"
+ type="date"
+ placeholder="閫夋嫨鐢熸晥鏃ユ湡"
+ style="width: 100%"
+ value-format="YYYY-MM-DD"
+ :disabled="priceStrategyDialogMode === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="澶辨晥鏃ユ湡" prop="endDate">
+ <el-date-picker
+ v-model="priceStrategyForm.endDate"
+ type="date"
+ 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="璇疯緭鍏ョ瓥鐣ヨ鏄�" :disabled="priceStrategyDialogMode === 'view'" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <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>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, nextTick, watch } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Document, Van, Tickets, Wallet, Check, Clock, Search } from '@element-plus/icons-vue'
+import * as echarts from 'echarts'
+import Pagination from '@/components/PIMTable/Pagination.vue'
+
+// 娲诲姩鏍囩椤�
+const activeTab = ref('priceStrategy')
+
+// ========== 浠锋牸绛栫暐閰嶇疆 ==========
+const priceLoading = ref(false)
+const priceSearchForm = reactive({
+ customerName: '',
+ productType: '',
+ strategyType: ''
+})
+
+const priceStrategyList = ref([
+ {
+ id: 1,
+ strategyNo: 'PS202501001',
+ strategyType: '涓撳睘浠锋牸',
+ customerName: '鍗庝笢寤烘潗闆嗗洟',
+ productName: 'P.O 42.5鏅�氱閰哥洂姘存偿',
+ specification: '50kg/琚�',
+ basePrice: 380,
+ strategyPrice: '楼350/鍚�',
+ startDate: '2025-01-01',
+ endDate: '2025-12-31',
+ status: '鐢熸晥涓�',
+ description: '鎴樼暐鍚堜綔瀹㈡埛涓撳睘浼樻儬浠锋牸'
+ },
+ {
+ id: 2,
+ strategyNo: 'PS202501002',
+ strategyType: '闃舵鎶ヤ环',
+ customerName: '闀挎睙娣峰嚌鍦熷叕鍙�',
+ productName: 'P.S 32.5鐭挎福纭呴吀鐩愭按娉�',
+ specification: '50kg/琚�',
+ basePrice: 320,
+ strategyPrice: '500鍚ㄤ互涓�9鎶�',
+ startDate: '2025-01-01',
+ endDate: '2025-06-30',
+ status: '鐢熸晥涓�',
+ description: '澶ф壒閲忛噰璐樁姊紭鎯�'
+ },
+ {
+ id: 3,
+ strategyNo: 'PS202501003',
+ strategyType: '淇冮攢鎶樻墸',
+ customerName: '娴︽睙姘存偿鍒跺搧鍘�',
+ productName: 'P.C 32.5澶嶅悎纭呴吀鐩愭按娉�',
+ specification: '50kg/琚�',
+ basePrice: 300,
+ strategyPrice: '8.5鎶�',
+ startDate: '2025-01-15',
+ endDate: '2025-02-28',
+ status: '鐢熸晥涓�',
+ description: '鏄ヨ妭淇冮攢娲诲姩'
+ },
+ {
+ id: 4,
+ strategyNo: 'PS202412015',
+ strategyType: '涓撳睘浠锋牸',
+ customerName: '鍗庝笢寤烘潗闆嗗洟',
+ productName: 'P.C 32.5澶嶅悎纭呴吀鐩愭按娉�',
+ specification: '50kg/琚�',
+ basePrice: 300,
+ strategyPrice: '楼285/鍚�',
+ startDate: '2024-10-01',
+ endDate: '2024-12-31',
+ status: '宸茶繃鏈�',
+ description: '绗洓瀛e害涓撳睘浠锋牸'
+ }
+])
+
+const pricePagination = reactive({
+ total: 4,
+ currentPage: 1,
+ pageSize: 10
+})
+
+const priceStrategyDialogVisible = ref(false)
+const priceStrategyDialogTitle = ref('鏂板浠锋牸绛栫暐')
+const priceStrategyDialogMode = ref('add') // add | edit | view
+const priceStrategyForm = reactive({
+ strategyType: '',
+ customerName: '',
+ productName: '',
+ specification: '',
+ basePrice: 0,
+ strategyPrice: '',
+ startDate: '',
+ endDate: '',
+ description: ''
+})
+
+const priceStrategyRules = {
+ strategyType: [{ required: true, message: '璇烽�夋嫨绛栫暐绫诲瀷', trigger: 'change' }],
+ customerName: [{ required: true, message: '璇烽�夋嫨瀹㈡埛', trigger: 'change' }],
+ productName: [{ required: true, message: '璇烽�夋嫨浜у搧', trigger: 'change' }],
+ basePrice: [{ required: true, message: '璇疯緭鍏ュ熀纭�浠锋牸', trigger: 'blur' }],
+ strategyPrice: [{ required: true, message: '璇疯緭鍏ョ瓥鐣ヤ环鏍�', trigger: 'blur' }],
+ startDate: [{ required: true, message: '璇烽�夋嫨鐢熸晥鏃ユ湡', trigger: 'change' }],
+ endDate: [{ required: true, message: '璇烽�夋嫨澶辨晥鏃ユ湡', trigger: 'change' }]
+}
+
+const priceStrategyFormRef = ref()
+
+// ========== 鍚堝悓鎵ц鐩戞帶 ==========
+const contractLoading = ref(false)
+const contractStats = reactive({
+ totalContracts: 48,
+ deliveryRate: 87.5,
+ invoiceRate: 82.3,
+ paymentRate: 75.6
+})
+
+const contractSearchForm = reactive({
+ contractNo: '',
+ customerName: '',
+ executionStatus: ''
+})
+
+const contractList = ref([
+ {
+ id: 1,
+ contractNo: 'CT202501001',
+ customerName: '鍗庝笢寤烘潗闆嗗洟',
+ contractAmount: 2850000,
+ signDate: '2025-01-05',
+ executionProgress: 85,
+ deliveryProgress: 90,
+ invoiceProgress: 85,
+ paymentProgress: 75,
+ executionStatus: '鎵ц涓�',
+ orderStatus: '宸插畬鎴�',
+ orderDate: '2025-01-05'
+ },
+ {
+ id: 2,
+ contractNo: 'CT202501002',
+ customerName: '闀挎睙娣峰嚌鍦熷叕鍙�',
+ contractAmount: 1650000,
+ signDate: '2025-01-08',
+ executionProgress: 95,
+ deliveryProgress: 100,
+ invoiceProgress: 100,
+ paymentProgress: 85,
+ executionStatus: '鎵ц涓�',
+ orderStatus: '宸插畬鎴�',
+ orderDate: '2025-01-08'
+ },
+ {
+ id: 3,
+ contractNo: 'CT202501003',
+ customerName: '娴︽睙姘存偿鍒跺搧鍘�',
+ contractAmount: 980000,
+ signDate: '2025-01-12',
+ executionProgress: 60,
+ deliveryProgress: 65,
+ invoiceProgress: 60,
+ paymentProgress: 50,
+ executionStatus: '鎵ц涓�',
+ orderStatus: '宸插畬鎴�',
+ orderDate: '2025-01-12'
+ },
+ {
+ id: 4,
+ contractNo: 'CT202412028',
+ customerName: '鍗庝笢寤烘潗闆嗗洟',
+ contractAmount: 3200000,
+ signDate: '2024-12-15',
+ executionProgress: 100,
+ deliveryProgress: 100,
+ invoiceProgress: 100,
+ paymentProgress: 100,
+ executionStatus: '宸插畬鎴�',
+ orderStatus: '宸插畬鎴�',
+ orderDate: '2024-12-15'
+ },
+ {
+ id: 5,
+ contractNo: 'CT202501004',
+ customerName: '闀挎睙娣峰嚌鍦熷叕鍙�',
+ contractAmount: 750000,
+ signDate: '2025-01-20',
+ executionProgress: 25,
+ deliveryProgress: 30,
+ invoiceProgress: 20,
+ paymentProgress: 0,
+ executionStatus: '寮傚父',
+ orderStatus: '宸插畬鎴�',
+ orderDate: '2025-01-20'
+ }
+])
+
+const contractPagination = reactive({
+ total: 5,
+ currentPage: 1,
+ pageSize: 10
+})
+
+// ========== 鍘嗗彶姣斾环鍒嗘瀽 ==========
+const compareLoading = ref(false)
+const compareSearchForm = reactive({
+ productName: '',
+ dateRange: [],
+ region: ''
+})
+
+const priceComparisonList = ref([
+ { date: '2025-01-20', productName: 'P.O 42.5鏅�氱閰哥洂姘存偿', specification: '50kg/琚�', customerName: '鍗庝笢寤烘潗闆嗗洟', region: '鍗庝笢鍦板尯', quantity: 5000, price: 350, totalAmount: 1750000, priceChange: 0, remark: '闀挎湡鍚堜綔瀹㈡埛' },
+ { date: '2025-01-15', productName: 'P.O 42.5鏅�氱閰哥洂姘存偿', specification: '50kg/琚�', customerName: '娴︿笢鏂板尯寤虹瓚鍏徃', region: '鍗庝笢鍦板尯', quantity: 3000, price: 365, totalAmount: 1095000, priceChange: +15, remark: '鐜版鐜拌揣' },
+ { date: '2025-01-10', productName: 'P.O 42.5鏅�氱閰哥洂姘存偿', specification: '50kg/琚�', customerName: '闀挎睙娣峰嚌鍦熷叕鍙�', region: '鍗庝笢鍦板尯', quantity: 8000, price: 345, totalAmount: 2760000, priceChange: -5, remark: '澶ф壒閲忎紭鎯�' },
+ { date: '2025-01-05', productName: 'P.O 42.5鏅�氱閰哥洂姘存偿', specification: '50kg/琚�', customerName: '姹熻嫃宸ョ▼闆嗗洟', region: '鍗庝笢鍦板尯', quantity: 4500, price: 360, totalAmount: 1620000, priceChange: +10, remark: '宸ョ▼椤圭洰涓撶敤' },
+ { date: '2024-12-28', productName: 'P.O 42.5鏅�氱閰哥洂姘存偿', specification: '50kg/琚�', customerName: '鍗庝笢寤烘潗闆嗗洟', region: '鍗庝笢鍦板尯', quantity: 6000, price: 355, totalAmount: 2130000, priceChange: +5, remark: '骞村簳澶囪揣' },
+ { date: '2024-12-20', productName: 'P.O 42.5鏅�氱閰哥洂姘存偿', specification: '50kg/琚�', customerName: '涓婃捣甯傛斂宸ョ▼', region: '鍗庝笢鍦板尯', quantity: 10000, price: 340, totalAmount: 3400000, priceChange: -10, remark: '鏀垮簻椤圭洰' }
+])
+
+const priceChartRef = ref(null)
+let priceChart = null
+
+// ========== 鍒╂鼎鍒嗘瀽 ==========
+const profitLoading = ref(false)
+const profitStats = reactive({
+ totalSales: 15680000,
+ totalCost: 11256000,
+ grossProfit: 4424000,
+ grossProfitRate: 28.2,
+ salesGrowth: 12.5,
+ costRate: 71.8
+})
+
+const profitSearchForm = reactive({
+ productType: '',
+ customerName: '',
+ dateRange: []
+})
+
+const profitAnalysisList = ref([
+ { orderNo: 'SO202501015', customerName: '鍗庝笢寤烘潗闆嗗洟', productName: 'P.O 42.5鏅�氱閰哥洂姘存偿', quantity: 5000, salesPrice: 350, costPrice: 245, salesAmount: 1750000, costAmount: 1225000, grossProfit: 525000, grossProfitRate: 30.0, orderDate: '2025-01-20' },
+ { orderNo: 'SO202501012', customerName: '闀挎睙娣峰嚌鍦熷叕鍙�', productName: 'P.S 32.5鐭挎福纭呴吀鐩愭按娉�', quantity: 3500, salesPrice: 288, costPrice: 210, salesAmount: 1008000, costAmount: 735000, grossProfit: 273000, grossProfitRate: 27.1, orderDate: '2025-01-18' },
+ { orderNo: 'SO202501008', customerName: '娴︽睙姘存偿鍒跺搧鍘�', productName: 'P.C 32.5澶嶅悎纭呴吀鐩愭按娉�', quantity: 2800, salesPrice: 255, costPrice: 185, salesAmount: 714000, costAmount: 518000, grossProfit: 196000, grossProfitRate: 27.5, orderDate: '2025-01-15' },
+ { orderNo: 'SO202501005', customerName: '鍗庝笢寤烘潗闆嗗洟', productName: 'P.O 42.5鏅�氱閰哥洂姘存偿', quantity: 6000, salesPrice: 350, costPrice: 248, salesAmount: 2100000, costAmount: 1488000, grossProfit: 612000, grossProfitRate: 29.1, orderDate: '2025-01-10' },
+ { orderNo: 'SO202501003', customerName: '姹熻嫃宸ョ▼闆嗗洟', productName: 'P.O 42.5鏅�氱閰哥洂姘存偿', quantity: 4500, salesPrice: 360, costPrice: 250, salesAmount: 1620000, costAmount: 1125000, grossProfit: 495000, grossProfitRate: 30.6, orderDate: '2025-01-08' },
+ { orderNo: 'SO202412025', customerName: '闀挎睙娣峰嚌鍦熷叕鍙�', productName: 'P.S 32.5鐭挎福纭呴吀鐩愭按娉�', quantity: 8000, salesPrice: 290, costPrice: 215, salesAmount: 2320000, costAmount: 1720000, grossProfit: 600000, grossProfitRate: 25.9, orderDate: '2024-12-28' }
+])
+
+const profitPagination = reactive({
+ total: 6,
+ currentPage: 1,
+ pageSize: 10
+})
+
+const profitChartRef = ref(null)
+let profitChart = null
+
+// ========== 鏂规硶 ==========
+
+// 浠锋牸绛栫暐鐩稿叧鏂规硶
+const getStrategyTypeColor = (type) => {
+ const colorMap = {
+ '涓撳睘浠锋牸': 'success',
+ '闃舵鎶ヤ环': 'primary',
+ '淇冮攢鎶樻墸': 'warning'
+ }
+ return colorMap[type] || 'info'
+}
+
+const searchPriceStrategy = () => {
+ priceLoading.value = true
+ setTimeout(() => {
+ priceLoading.value = false
+ }, 500)
+}
+
+const resetPriceSearch = () => {
+ priceSearchForm.customerName = ''
+ priceSearchForm.productType = ''
+ priceSearchForm.strategyType = ''
+}
+
+const handleAddPriceStrategy = () => {
+ priceStrategyDialogTitle.value = '鏂板浠锋牸绛栫暐'
+ resetPriceStrategyForm()
+ priceStrategyDialogMode.value = 'add'
+ priceStrategyDialogVisible.value = true
+}
+
+const handleViewPriceStrategy = (row) => {
+ 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
+}
+
+const handleDeletePriceStrategy = (row) => {
+ ElMessageBox.confirm('纭鍒犻櫎璇ヤ环鏍肩瓥鐣ュ悧锛�', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ 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('鍒犻櫎鎴愬姛')
+ })
+}
+
+const resetPriceStrategyForm = () => {
+ Object.keys(priceStrategyForm).forEach(key => {
+ if (key === 'basePrice') {
+ priceStrategyForm[key] = 0
+ } else {
+ priceStrategyForm[key] = ''
+ }
+ })
+}
+
+const handleSavePriceStrategy = () => {
+ priceStrategyFormRef.value.validate((valid) => {
+ if (valid) {
+ ElMessage.success('淇濆瓨鎴愬姛')
+ priceStrategyDialogVisible.value = false
+ }
+ })
+}
+
+const handlePricePageChange = (val) => {
+ pricePagination.currentPage = val.page
+ pricePagination.pageSize = val.limit
+}
+
+// 鍚堝悓鎵ц鐩戞帶鐩稿叧鏂规硶
+const getExecutionStatusType = (status) => {
+ const statusMap = {
+ '寰呮墽琛�': 'info',
+ '鎵ц涓�': 'primary',
+ '宸插畬鎴�': 'success',
+ '寮傚父': 'danger'
+ }
+ return statusMap[status] || 'info'
+}
+
+const getProgressColor = (percentage) => {
+ if (percentage < 30) return '#f56c6c'
+ if (percentage < 70) return '#e6a23c'
+ return '#67c23a'
+}
+
+const getContractStep = (row) => {
+ if (row.paymentProgress === 100) return 4
+ if (row.invoiceProgress === 100) return 3
+ if (row.deliveryProgress === 100) return 2
+ if (row.orderStatus === '宸插畬鎴�') return 1
+ return 0
+}
+
+const searchContract = () => {
+ contractLoading.value = true
+ setTimeout(() => {
+ contractLoading.value = false
+ }, 500)
+}
+
+const resetContractSearch = () => {
+ contractSearchForm.contractNo = ''
+ contractSearchForm.customerName = ''
+ contractSearchForm.executionStatus = ''
+}
+
+const handleViewContract = (row) => {
+ ElMessage.info('鏌ョ湅鍚堝悓璇︽儏: ' + row.contractNo)
+}
+
+const handleContractPageChange = (val) => {
+ contractPagination.currentPage = val.page
+ contractPagination.pageSize = val.limit
+}
+
+// 鍘嗗彶姣斾环鍒嗘瀽鐩稿叧鏂规硶
+const searchPriceComparison = () => {
+ compareLoading.value = true
+ setTimeout(() => {
+ compareLoading.value = false
+ initPriceChart()
+ }, 500)
+}
+
+const resetCompareSearch = () => {
+ compareSearchForm.productName = ''
+ compareSearchForm.dateRange = []
+ compareSearchForm.region = ''
+}
+
+const initPriceChart = () => {
+ if (!priceChartRef.value) return
+
+ if (priceChart) {
+ priceChart.dispose()
+ }
+
+ priceChart = echarts.init(priceChartRef.value)
+
+ const option = {
+ title: {
+ text: '姘存偿浠锋牸瓒嬪娍鍒嗘瀽',
+ left: 'center'
+ },
+ tooltip: {
+ trigger: 'axis',
+ formatter: '{b}<br/>{a}: 楼{c}/鍚�'
+ },
+ legend: {
+ data: ['P.O 42.5鏅�氱閰哥洂姘存偿'],
+ top: 30
+ },
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: ['2024-12-20', '2024-12-28', '2025-01-05', '2025-01-10', '2025-01-15', '2025-01-20']
+ },
+ yAxis: {
+ type: 'value',
+ name: '浠锋牸(鍏�/鍚�)',
+ min: 330,
+ max: 370
+ },
+ series: [
+ {
+ name: 'P.O 42.5鏅�氱閰哥洂姘存偿',
+ type: 'line',
+ data: [340, 355, 360, 345, 365, 350],
+ smooth: true,
+ itemStyle: {
+ color: '#409eff'
+ },
+ areaStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: 'rgba(64, 158, 255, 0.3)' },
+ { offset: 1, color: 'rgba(64, 158, 255, 0.1)' }
+ ])
+ }
+ }
+ ]
+ }
+
+ priceChart.setOption(option)
+}
+
+// 鍒╂鼎鍒嗘瀽鐩稿叧鏂规硶
+const searchProfit = () => {
+ profitLoading.value = true
+ setTimeout(() => {
+ profitLoading.value = false
+ initProfitChart()
+ }, 500)
+}
+
+const resetProfitSearch = () => {
+ profitSearchForm.productType = ''
+ profitSearchForm.customerName = ''
+ profitSearchForm.dateRange = []
+}
+
+const getProfitRateType = (rate) => {
+ if (rate >= 30) return 'success'
+ if (rate >= 25) return 'warning'
+ return 'danger'
+}
+
+const getProfitSummary = (param) => {
+ const { columns, data } = param
+ const sums = []
+ columns.forEach((column, index) => {
+ if (index === 0) {
+ sums[index] = '鍚堣'
+ return
+ }
+ if (['quantity', 'salesAmount', 'costAmount', 'grossProfit'].includes(column.property)) {
+ const values = data.map(item => Number(item[column.property]))
+ if (!values.every(value => isNaN(value))) {
+ const total = values.reduce((prev, curr) => {
+ const value = Number(curr)
+ if (!isNaN(value)) {
+ return prev + curr
+ } else {
+ return prev
+ }
+ }, 0)
+ sums[index] = column.property === 'quantity' ? total.toLocaleString() : '楼' + total.toLocaleString()
+ }
+ } else if (column.property === 'grossProfitRate') {
+ // 璁$畻骞冲潎姣涘埄鐜�
+ const totalSales = data.reduce((sum, item) => sum + item.salesAmount, 0)
+ const totalProfit = data.reduce((sum, item) => sum + item.grossProfit, 0)
+ sums[index] = ((totalProfit / totalSales) * 100).toFixed(1) + '%'
+ }
+ })
+ return sums
+}
+
+const initProfitChart = () => {
+ if (!profitChartRef.value) return
+
+ if (profitChart) {
+ profitChart.dispose()
+ }
+
+ profitChart = echarts.init(profitChartRef.value)
+
+ const option = {
+ title: {
+ text: '閿�鍞笌鍒╂鼎瓒嬪娍鍒嗘瀽',
+ left: 'center'
+ },
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'cross',
+ crossStyle: {
+ color: '#999'
+ }
+ }
+ },
+ legend: {
+ data: ['閿�鍞噾棰�', '鎴愭湰閲戦', '姣涘埄娑�', '姣涘埄鐜�'],
+ top: 30
+ },
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true
+ },
+ xAxis: [
+ {
+ type: 'category',
+ data: ['2024-12', '2025-01'],
+ axisPointer: {
+ type: 'shadow'
+ }
+ }
+ ],
+ yAxis: [
+ {
+ type: 'value',
+ name: '閲戦(涓囧厓)',
+ axisLabel: {
+ formatter: '{value}'
+ }
+ },
+ {
+ type: 'value',
+ name: '姣涘埄鐜�(%)',
+ min: 0,
+ max: 40,
+ axisLabel: {
+ formatter: '{value}%'
+ }
+ }
+ ],
+ series: [
+ {
+ name: '閿�鍞噾棰�',
+ type: 'bar',
+ data: [820, 950],
+ itemStyle: {
+ color: '#409eff'
+ }
+ },
+ {
+ name: '鎴愭湰閲戦',
+ type: 'bar',
+ data: [605, 670],
+ itemStyle: {
+ color: '#e6a23c'
+ }
+ },
+ {
+ name: '姣涘埄娑�',
+ type: 'bar',
+ data: [215, 280],
+ itemStyle: {
+ color: '#67c23a'
+ }
+ },
+ {
+ name: '姣涘埄鐜�',
+ type: 'line',
+ yAxisIndex: 1,
+ data: [26.2, 29.5],
+ itemStyle: {
+ color: '#f56c6c'
+ }
+ }
+ ]
+ }
+
+ profitChart.setOption(option)
+}
+
+const handleProfitPageChange = (val) => {
+ profitPagination.currentPage = val.page
+ 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(() => {
+ // 缁勪欢鎸傝浇鍚庝笉绔嬪嵆鍒濆鍖栧浘琛紝绛夊緟鐢ㄦ埛鍒囨崲鍒板搴旀爣绛鹃〉
+})
+
+// 鐩戝惉鏍囩椤靛垏鎹�
+watch(activeTab, (newVal) => {
+ nextTick(() => {
+ if (newVal === 'priceComparison') {
+ initPriceChart()
+ } else if (newVal === 'profitAnalysis') {
+ initProfitChart()
+ } else if (newVal === 'indicatorStats') {
+ initIndicatorChart()
+ }
+ })
+})
+
+const handleTabChange = () => {
+ // 鏍囩椤靛垏鎹㈠鐞�
+}
+</script>
+
+<style scoped>
+.strategy-control {
+ padding: 0;
+}
+
+.main-tabs {
+ border: none;
+ box-shadow: none;
+}
+
+.main-tabs :deep(.el-tabs__content) {
+ padding: 0;
+}
+
+.box-card {
+ border: none;
+ box-shadow: none;
+}
+
+.search-row {
+ margin-bottom: 20px;
+}
+
+/* 缁熻鍗$墖鏍峰紡 */
+.stats-row {
+ margin-bottom: 24px;
+}
+
+.stat-card {
+ display: flex;
+ align-items: center;
+ padding: 20px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+
+.stat-icon {
+ width: 60px;
+ height: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 8px;
+ margin-right: 16px;
+}
+
+.stat-content {
+ flex: 1;
+}
+
+.stat-value {
+ font-size: 28px;
+ font-weight: bold;
+ color: #303133;
+ margin-bottom: 4px;
+}
+
+.stat-label {
+ font-size: 14px;
+ color: #909399;
+}
+
+/* 鍚堝悓璇︽儏灞曞紑鏍峰紡 */
+.contract-detail-expand {
+ padding: 30px 60px;
+ background: #f5f7fa;
+}
+
+.contract-detail-expand :deep(.el-step__title) {
+ font-size: 14px;
+}
+
+.contract-detail-expand :deep(.el-step__description) {
+ font-size: 12px;
+ margin-top: 4px;
+}
+
+/* 鍒╂鼎缁熻鍗$墖 */
+.profit-stats-row {
+ margin-bottom: 24px;
+}
+
+.profit-card {
+ padding: 24px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border-radius: 12px;
+ color: #fff;
+ box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
+}
+
+.profit-card:nth-child(2) .profit-card {
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
+}
+
+.profit-card:nth-child(3) .profit-card {
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+}
+
+.profit-header {
+ font-size: 14px;
+ opacity: 0.9;
+ margin-bottom: 12px;
+}
+
+.profit-value {
+ font-size: 32px;
+ font-weight: bold;
+ margin-bottom: 12px;
+}
+
+.profit-highlight {
+ color: #fff;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+}
+
+.profit-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 13px;
+ opacity: 0.9;
+}
+
+.growth-up {
+ color: #fff;
+ font-weight: bold;
+}
+
+.growth-down {
+ color: #ffd04b;
+ font-weight: bold;
+}
+
+.cost-rate, .gross-profit-rate {
+ font-weight: bold;
+}
+
+/* 鍥捐〃瀹瑰櫒 */
+.chart-container {
+ margin: 20px 0;
+ padding: 20px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+</style>
+
--
Gitblit v1.9.3