From 4650de835add34d2193d52c620523d43ec4f50a9 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 29 十月 2025 15:21:13 +0800
Subject: [PATCH] 汇星与博达-策略管控前端页面
---
src/api/salesManagement/strategyControl.js | 202 +++++++
src/views/salesManagement/strategyControl/index.vue | 1347 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1,549 insertions(+), 0 deletions(-)
diff --git a/src/api/salesManagement/strategyControl.js b/src/api/salesManagement/strategyControl.js
new file mode 100644
index 0000000..d86864e
--- /dev/null
+++ b/src/api/salesManagement/strategyControl.js
@@ -0,0 +1,202 @@
+// 绛栫暐绠℃帶椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// ========== 浠锋牸绛栫暐閰嶇疆 ==========
+
+// 鍒嗛〉鏌ヨ浠锋牸绛栫暐鍒楄〃
+export function getPriceStrategyList(query) {
+ return request({
+ url: "/sales/priceStrategy/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ浠锋牸绛栫暐璇︽儏
+export function getPriceStrategyDetail(id) {
+ return request({
+ url: "/sales/priceStrategy/detail",
+ method: "get",
+ params: { id },
+ });
+}
+
+// 鏂板浠锋牸绛栫暐
+export function addPriceStrategy(data) {
+ return request({
+ url: "/sales/priceStrategy/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 淇敼浠锋牸绛栫暐
+export function updatePriceStrategy(data) {
+ return request({
+ url: "/sales/priceStrategy/update",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鍒犻櫎浠锋牸绛栫暐
+export function deletePriceStrategy(id) {
+ return request({
+ url: "/sales/priceStrategy/delete",
+ method: "delete",
+ params: { id },
+ });
+}
+
+// 鍚敤/绂佺敤浠锋牸绛栫暐
+export function togglePriceStrategy(data) {
+ return request({
+ url: "/sales/priceStrategy/toggle",
+ method: "post",
+ data: data,
+ });
+}
+
+// ========== 鍚堝悓鎵ц鐩戞帶 ==========
+
+// 鑾峰彇鍚堝悓鎵ц缁熻鏁版嵁
+export function getContractStats(query) {
+ return request({
+ url: "/sales/contract/stats",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鍒嗛〉鏌ヨ鍚堝悓鎵ц鍒楄〃
+export function getContractExecutionList(query) {
+ return request({
+ url: "/sales/contract/executionList",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ鍚堝悓鎵ц璇︽儏
+export function getContractExecutionDetail(contractNo) {
+ return request({
+ url: "/sales/contract/executionDetail",
+ method: "get",
+ params: { contractNo },
+ });
+}
+
+// 鏇存柊鍚堝悓鎵ц杩涘害
+export function updateContractProgress(data) {
+ return request({
+ url: "/sales/contract/updateProgress",
+ method: "post",
+ data: data,
+ });
+}
+
+// ========== 鍘嗗彶姣斾环鍒嗘瀽 ==========
+
+// 鏌ヨ鍘嗗彶浠锋牸瀵规瘮鏁版嵁
+export function getPriceComparisonList(query) {
+ return request({
+ url: "/sales/priceComparison/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鑾峰彇浠锋牸瓒嬪娍鍥捐〃鏁版嵁
+export function getPriceTrendChart(query) {
+ return request({
+ url: "/sales/priceComparison/trendChart",
+ method: "get",
+ params: query,
+ });
+}
+
+// 瀵煎嚭鍘嗗彶姣斾环鏁版嵁
+export function exportPriceComparison(query) {
+ return request({
+ url: "/sales/priceComparison/export",
+ method: "get",
+ params: query,
+ responseType: "blob",
+ });
+}
+
+// ========== 鍒╂鼎鍒嗘瀽 ==========
+
+// 鑾峰彇鍒╂鼎缁熻鏁版嵁
+export function getProfitStats(query) {
+ return request({
+ url: "/sales/profit/stats",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鍒嗛〉鏌ヨ鍒╂鼎鍒嗘瀽鍒楄〃
+export function getProfitAnalysisList(query) {
+ return request({
+ url: "/sales/profit/analysisList",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鑾峰彇鍒╂鼎瓒嬪娍鍥捐〃鏁版嵁
+export function getProfitTrendChart(query) {
+ return request({
+ url: "/sales/profit/trendChart",
+ method: "get",
+ params: query,
+ });
+}
+
+// 璁$畻姣涘埄鐜�
+export function calculateGrossProfit(data) {
+ return request({
+ url: "/sales/profit/calculate",
+ method: "post",
+ data: data,
+ });
+}
+
+// 瀵煎嚭鍒╂鼎鍒嗘瀽鎶ヨ〃
+export function exportProfitAnalysis(query) {
+ return request({
+ url: "/sales/profit/export",
+ method: "get",
+ params: query,
+ responseType: "blob",
+ });
+}
+
+// ========== 鍏叡鎺ュ彛 ==========
+
+// 鏌ヨ瀹㈡埛鍒楄〃锛堢敤浜庝笅鎷夐�夋嫨锛�
+export function getCustomerOptions() {
+ return request({
+ url: "/basic/customer/options",
+ method: "get",
+ });
+}
+
+// 鏌ヨ浜у搧鍒楄〃锛堢敤浜庝笅鎷夐�夋嫨锛�
+export function getProductOptions(query) {
+ return request({
+ url: "/basic/product/options",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ閿�鍞尯鍩熷垪琛�
+export function getRegionOptions() {
+ return request({
+ url: "/basic/region/options",
+ method: "get",
+ });
+}
+
diff --git a/src/views/salesManagement/strategyControl/index.vue b/src/views/salesManagement/strategyControl/index.vue
new file mode 100644
index 0000000..4f5f90d
--- /dev/null
+++ b/src/views/salesManagement/strategyControl/index.vue
@@ -0,0 +1,1347 @@
+<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-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%;">
+ <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%;">
+ <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%;">
+ <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="璇疯緭鍏ヨ鏍煎瀷鍙�" />
+ </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-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="绛栫暐浠锋牸" prop="strategyPrice">
+ <el-input v-model="priceStrategyForm.strategyPrice" placeholder="濡�: 楼350/鍚� 鎴� 9鎶�" />
+ </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"
+ />
+ </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"
+ />
+ </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-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="priceStrategyDialogVisible = false">鍙栨秷</el-button>
+ <el-button 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 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()
+ priceStrategyDialogVisible.value = true
+}
+
+const handleViewPriceStrategy = (row) => {
+ ElMessage.info('鏌ョ湅绛栫暐璇︽儏: ' + row.strategyNo)
+}
+
+const handleEditPriceStrategy = (row) => {
+ priceStrategyDialogTitle.value = '缂栬緫浠锋牸绛栫暐'
+ Object.assign(priceStrategyForm, row)
+ priceStrategyDialogVisible.value = true
+}
+
+const handleDeletePriceStrategy = (row) => {
+ ElMessageBox.confirm('纭鍒犻櫎璇ヤ环鏍肩瓥鐣ュ悧锛�', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => {
+ 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
+}
+
+// 鐢熷懡鍛ㄦ湡
+onMounted(() => {
+ // 缁勪欢鎸傝浇鍚庝笉绔嬪嵆鍒濆鍖栧浘琛紝绛夊緟鐢ㄦ埛鍒囨崲鍒板搴旀爣绛鹃〉
+})
+
+// 鐩戝惉鏍囩椤靛垏鎹�
+watch(activeTab, (newVal) => {
+ nextTick(() => {
+ if (newVal === 'priceComparison') {
+ initPriceChart()
+ } else if (newVal === 'profitAnalysis') {
+ initProfitChart()
+ }
+ })
+})
+
+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