From 28c8c43997baa2dc9301ee75308afa762815890f Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 17 九月 2025 14:15:34 +0800
Subject: [PATCH] 采购价格管理前端页面

---
 src/api/procurementManagement/advancedPriceManagement.js          |  302 +++++++++++++++
 src/views/procurementManagement/advancedPriceManagement/index.vue |  874 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1,176 insertions(+), 0 deletions(-)

diff --git a/src/api/procurementManagement/advancedPriceManagement.js b/src/api/procurementManagement/advancedPriceManagement.js
new file mode 100644
index 0000000..c1dd47c
--- /dev/null
+++ b/src/api/procurementManagement/advancedPriceManagement.js
@@ -0,0 +1,302 @@
+// 楂樼骇閲囪喘浠锋牸绠$悊API鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ浠锋牸鍒楄〃
+export function getPriceList(query) {
+  return request({
+    url: "/procurement/price/list",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鑾峰彇浠锋牸璇︽儏
+export function getPriceDetail(id) {
+  return request({
+    url: `/procurement/price/detail/${id}`,
+    method: "get",
+  });
+}
+
+// 鏂板浠锋牸
+export function addPrice(data) {
+  return request({
+    url: "/procurement/price/add",
+    method: "post",
+    data: data,
+  });
+}
+
+// 鏇存柊浠锋牸
+export function updatePrice(data) {
+  return request({
+    url: "/procurement/price/update",
+    method: "put",
+    data: data,
+  });
+}
+
+// 鍒犻櫎浠锋牸
+export function deletePrice(id) {
+  return request({
+    url: `/procurement/price/delete/${id}`,
+    method: "delete",
+  });
+}
+
+// 鎵归噺鍒犻櫎浠锋牸
+export function batchDeletePrice(ids) {
+  return request({
+    url: "/procurement/price/batchDelete",
+    method: "delete",
+    data: { ids },
+  });
+}
+
+// 澶嶅埗浠锋牸
+export function copyPrice(id) {
+  return request({
+    url: `/procurement/price/copy/${id}`,
+    method: "post",
+  });
+}
+
+// 搴旂敤浠锋牸锛堝皢寰呯敓鏁堢姸鎬佹敼涓烘湁鏁堬級
+export function applyPrice(id) {
+  return request({
+    url: `/procurement/price/apply/${id}`,
+    method: "put",
+  });
+}
+
+// 鏆傚仠浠锋牸
+export function suspendPrice(id) {
+  return request({
+    url: `/procurement/price/suspend/${id}`,
+    method: "put",
+  });
+}
+
+// 鎵归噺璁剧疆鎶樻墸
+export function batchSetDiscount(data) {
+  return request({
+    url: "/procurement/price/batchDiscount",
+    method: "post",
+    data: data,
+  });
+}
+
+// 鑾峰彇鎶樻墸閰嶇疆
+export function getDiscountConfig(id) {
+  return request({
+    url: `/procurement/price/discount/${id}`,
+    method: "get",
+  });
+}
+
+// 璁剧疆鍗曚釜鍟嗗搧鎶樻墸
+export function setDiscount(data) {
+  return request({
+    url: "/procurement/price/setDiscount",
+    method: "post",
+    data: data,
+  });
+}
+
+// 鑾峰彇闃舵鎶樻墸閰嶇疆
+export function getTieredDiscount(id) {
+  return request({
+    url: `/procurement/price/tieredDiscount/${id}`,
+    method: "get",
+  });
+}
+
+// 璁剧疆闃舵鎶樻墸
+export function setTieredDiscount(data) {
+  return request({
+    url: "/procurement/price/setTieredDiscount",
+    method: "post",
+    data: data,
+  });
+}
+
+// 鑾峰彇浠锋牸鎺у埗璁剧疆
+export function getPriceControlConfig() {
+  return request({
+    url: "/procurement/price/controlConfig",
+    method: "get",
+  });
+}
+
+// 鏇存柊浠锋牸鎺у埗璁剧疆
+export function updatePriceControlConfig(data) {
+  return request({
+    url: "/procurement/price/controlConfig",
+    method: "put",
+    data: data,
+  });
+}
+
+// 鑾峰彇浠锋牸棰勮鍒楄〃
+export function getPriceWarnings(query) {
+  return request({
+    url: "/procurement/price/warnings",
+    method: "get",
+    params: query,
+  });
+}
+
+// 澶勭悊浠锋牸棰勮
+export function handlePriceWarning(id, action) {
+  return request({
+    url: `/procurement/price/warning/${id}`,
+    method: "put",
+    data: { action },
+  });
+}
+
+// 鑾峰彇浠锋牸鍘嗗彶璁板綍
+export function getPriceHistory(id, query) {
+  return request({
+    url: `/procurement/price/history/${id}`,
+    method: "get",
+    params: query,
+  });
+}
+
+// 鑾峰彇浠锋牸缁熻鏁版嵁
+export function getPriceStatistics(query) {
+  return request({
+    url: "/procurement/price/statistics",
+    method: "get",
+    params: query,
+  });
+}
+
+// 瀵煎嚭浠锋牸鏁版嵁
+export function exportPriceData(query) {
+  return request({
+    url: "/procurement/price/export",
+    method: "get",
+    params: query,
+    responseType: 'blob',
+  });
+}
+
+// 瀵煎叆浠锋牸鏁版嵁
+export function importPriceData(file) {
+  const formData = new FormData();
+  formData.append('file', file);
+  return request({
+    url: "/procurement/price/import",
+    method: "post",
+    data: formData,
+    headers: {
+      'Content-Type': 'multipart/form-data',
+    },
+  });
+}
+
+// 鑾峰彇浠锋牸妯℃澘
+export function downloadPriceTemplate() {
+  return request({
+    url: "/procurement/price/template",
+    method: "get",
+    responseType: 'blob',
+  });
+}
+
+// 浠锋牸瀹℃壒
+export function approvePrice(id, data) {
+  return request({
+    url: `/procurement/price/approve/${id}`,
+    method: "put",
+    data: data,
+  });
+}
+
+// 浠锋牸椹冲洖
+export function rejectPrice(id, data) {
+  return request({
+    url: `/procurement/price/reject/${id}`,
+    method: "put",
+    data: data,
+  });
+}
+
+// 鑾峰彇渚涘簲鍟嗗垪琛紙鐢ㄤ簬涓嬫媺閫夋嫨锛�
+export function getSupplierOptions() {
+  return request({
+    url: "/procurement/price/suppliers",
+    method: "get",
+  });
+}
+
+// 鑾峰彇鍟嗗搧鍒楄〃锛堢敤浜庝笅鎷夐�夋嫨锛�
+export function getProductOptions(query) {
+  return request({
+    url: "/procurement/price/products",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鑾峰彇鍟嗗搧璇︾粏淇℃伅
+export function getProductInfo(productId) {
+  return request({
+    url: `/procurement/price/productInfo/${productId}`,
+    method: "get",
+  });
+}
+
+// 浠锋牸姣旇緝鍒嗘瀽
+export function comparePrices(data) {
+  return request({
+    url: "/procurement/price/compare",
+    method: "post",
+    data: data,
+  });
+}
+
+// 鑾峰彇浠锋牸瓒嬪娍鏁版嵁
+export function getPriceTrend(id, period) {
+  return request({
+    url: `/procurement/price/trend/${id}`,
+    method: "get",
+    params: { period },
+  });
+}
+
+// 浠锋牸棰勬祴
+export function predictPrice(id, data) {
+  return request({
+    url: `/procurement/price/predict/${id}`,
+    method: "post",
+    data: data,
+  });
+}
+
+// 鑾峰彇甯傚満浠锋牸鍙傝��
+export function getMarketPriceReference(productCode) {
+  return request({
+    url: `/procurement/price/marketRef/${productCode}`,
+    method: "get",
+  });
+}
+
+// 浠锋牸鍙樺姩閫氱煡璁剧疆
+export function updateNotificationSettings(data) {
+  return request({
+    url: "/procurement/price/notifications",
+    method: "put",
+    data: data,
+  });
+}
+
+// 鑾峰彇浠锋牸鍙樺姩閫氱煡璁剧疆
+export function getNotificationSettings() {
+  return request({
+    url: "/procurement/price/notifications",
+    method: "get",
+  });
+}
diff --git a/src/views/procurementManagement/advancedPriceManagement/index.vue b/src/views/procurementManagement/advancedPriceManagement/index.vue
new file mode 100644
index 0000000..89fbc4b
--- /dev/null
+++ b/src/views/procurementManagement/advancedPriceManagement/index.vue
@@ -0,0 +1,874 @@
+<template>
+  <div class="app-container">
+    <!-- 鎼滅储鍖哄煙 -->
+    <el-card class="search-card" shadow="never">
+      <el-form :model="searchForm" :inline="true" label-width="100px">
+        <el-form-item label="鍟嗗搧鍚嶇О锛�">
+          <el-input v-model="searchForm.productName" placeholder="璇疯緭鍏ュ晢鍝佸悕绉�" clearable style="width: 200px" />
+        </el-form-item>
+        <el-form-item label="渚涘簲鍟嗭細">
+          <el-select v-model="searchForm.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" clearable style="width: 200px">
+            <el-option v-for="supplier in supplierList" :key="supplier.id" :label="supplier.name" :value="supplier.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="浠锋牸鐘舵�侊細">
+          <el-select v-model="searchForm.priceStatus" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 150px">
+            <el-option label="鏈夋晥" value="active" />
+            <el-option label="寰呯敓鏁�" value="pending" />
+            <el-option label="宸茶繃鏈�" value="expired" />
+            <el-option label="宸叉殏鍋�" value="suspended" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSearch" :loading="loading">
+            <el-icon><Search /></el-icon>
+            鎼滅储
+          </el-button>
+          <el-button @click="resetSearch">
+            <el-icon><Refresh /></el-icon>
+            閲嶇疆
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- 鍔熻兘鎸夐挳鍖哄煙 -->
+    <el-card class="action-card" shadow="never">
+      <div class="action-buttons">
+        <el-button type="primary" @click="openDialog('add')">
+          <el-icon><Plus /></el-icon>
+          鏂板浠锋牸
+        </el-button>
+        <el-button type="success" @click="openBatchDiscountDialog">
+          <el-icon><Discount /></el-icon>
+          鎵归噺鎶樻墸
+        </el-button>
+        <el-button type="warning" @click="openPriceControlDialog">
+          <el-icon><Setting /></el-icon>
+          浠锋牸鎺у埗
+        </el-button>
+        <el-button type="info" @click="exportData">
+          <el-icon><Download /></el-icon>
+          瀵煎嚭鏁版嵁
+        </el-button>
+        <el-button type="danger" @click="handleBatchDelete" :disabled="selectedRows.length === 0">
+          <el-icon><Delete /></el-icon>
+          鎵归噺鍒犻櫎
+        </el-button>
+      </div>
+    </el-card>
+
+
+    <!-- 涓昏〃鏍� -->
+    <el-card class="table-card" shadow="never">
+      <el-table 
+        :data="tableData" 
+        border 
+        v-loading="loading" 
+        @selection-change="handleSelectionChange"
+        :default-sort="{ prop: 'updateTime', order: 'descending' }"
+      >
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="鍟嗗搧淇℃伅" min-width="200">
+          <template #default="{ row }">
+            <div class="product-info">
+              <div class="product-name">{{ row.productName }}</div>
+              <div class="product-spec">{{ row.specification }}</div>
+              <div class="product-code">缂栫爜: {{ row.productCode }}</div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="渚涘簲鍟�" prop="supplierName" width="150" />
+        <el-table-column label="鍩虹浠锋牸" width="120" align="right">
+          <template #default="{ row }">
+            <span class="price-text">楼{{ row.basePrice.toFixed(2) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎶樻墸淇℃伅" width="150">
+          <template #default="{ row }">
+            <div v-if="row.discountType">
+              <el-tag :type="getDiscountTagType(row.discountType)" size="small">
+                {{ getDiscountText(row.discountType) }}
+              </el-tag>
+              <div class="discount-value">{{ row.discountValue }}{{ row.discountType === 'percentage' ? '%' : '鍏�' }}</div>
+            </div>
+            <span v-else class="no-discount">鏃犳姌鎵�</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="瀹為檯浠锋牸" width="120" align="right">
+          <template #default="{ row }">
+            <span class="final-price">楼{{ calculateFinalPrice(row).toFixed(2) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="浠锋牸鎺у埗" width="120">
+          <template #default="{ row }">
+            <div class="price-control">
+              <div v-if="row.priceControl?.minPrice" class="control-item">
+                鏈�浣�: 楼{{ row.priceControl.minPrice.toFixed(2) }}
+              </div>
+              <div v-if="row.priceControl?.maxPrice" class="control-item">
+                鏈�楂�: 楼{{ row.priceControl.maxPrice.toFixed(2) }}
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="鐘舵��" width="100" align="center">
+          <template #default="{ row }">
+            <el-tag :type="getStatusType(row.status)">{{ getStatusText(row.status) }}</el-tag>
+            <div v-if="isPriceWarning(row)" class="warning-indicator">
+              <el-icon color="#F56C6C"><Warning /></el-icon>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="鐢熸晥鏃堕棿" prop="effectiveTime" width="180" />
+        <el-table-column label="鏇存柊鏃堕棿" prop="updateTime" width="180" sortable />
+        <el-table-column label="鎿嶄綔" width="250" align="center" fixed="right">
+          <template #default="{ row }">
+            <el-button type="primary" link @click="openDialog('edit', row)">
+              <el-icon><Edit /></el-icon>
+              缂栬緫
+            </el-button>
+            <el-button type="success" link @click="openDiscountDialog(row)">
+              <el-icon><Discount /></el-icon>
+              鎶樻墸
+            </el-button>
+            <el-button type="danger" link @click="handleDelete(row)">
+              <el-icon><Delete /></el-icon>
+              鍒犻櫎
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 鍒嗛〉 -->
+      <div class="pagination-wrapper">
+        <el-pagination
+          v-model:current-page="pagination.currentPage"
+          v-model:page-size="pagination.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+
+    <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog v-model="dialogVisible" :title="dialogType === 'add' ? '鏂板浠锋牸' : '缂栬緫浠锋牸'" width="800px">
+      <el-form :model="formData" :rules="formRules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍟嗗搧鍚嶇О" prop="productName">
+              <el-select v-model="formData.productName" placeholder="璇烽�夋嫨鍟嗗搧" style="width: 100%" filterable>
+                <el-option v-for="product in productList" :key="product.id" :label="product.name" :value="product.name" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍟嗗搧缂栫爜" prop="productCode">
+              <el-input v-model="formData.productCode" placeholder="璇疯緭鍏ュ晢鍝佺紪鐮�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="瑙勬牸鍨嬪彿" prop="specification">
+              <el-input v-model="formData.specification" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="渚涘簲鍟�" prop="supplierName">
+              <el-select v-model="formData.supplierName" placeholder="璇烽�夋嫨渚涘簲鍟�" style="width: 100%">
+                <el-option v-for="supplier in supplierList" :key="supplier.id" :label="supplier.name" :value="supplier.name" />
+              </el-select>
+            </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="formData.basePrice" :min="0" :precision="2" placeholder="璇疯緭鍏ュ熀纭�浠锋牸" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍗曚綅">
+              <el-input v-model="formData.unit" placeholder="璇疯緭鍏ュ崟浣�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        
+        <!-- 鎶樻墸璁剧疆 -->
+        <el-divider content-position="left">鎶樻墸璁剧疆</el-divider>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="鎶樻墸绫诲瀷">
+              <el-select v-model="formData.discountType" placeholder="璇烽�夋嫨鎶樻墸绫诲瀷" style="width: 100%">
+                <el-option label="鏃犳姌鎵�" value="" />
+                <el-option label="鐧惧垎姣旀姌鎵�" value="percentage" />
+                <el-option label="鍥哄畾閲戦" value="fixed" />
+                <el-option label="闃舵鎶樻墸" value="tiered" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="鎶樻墸鍊�" v-if="formData.discountType && formData.discountType !== 'tiered'">
+              <el-input-number 
+                v-model="formData.discountValue" 
+                :min="0" 
+                :max="formData.discountType === 'percentage' ? 100 : undefined"
+                :precision="2" 
+                placeholder="璇疯緭鍏ユ姌鎵e��" 
+                style="width: 100%" 
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="鎶樻墸鏈夋晥鏈�">
+              <el-date-picker 
+                v-model="formData.discountEndTime" 
+                type="datetime" 
+                placeholder="閫夋嫨缁撴潫鏃堕棿" 
+                style="width: 100%" 
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 闃舵鎶樻墸璁剧疆 -->
+        <div v-if="formData.discountType === 'tiered'">
+          <el-form-item label="闃舵鎶樻墸">
+            <el-table :data="formData.tieredDiscount" border size="small">
+              <el-table-column label="鏈�灏忔暟閲�" width="120">
+                <template #default="{ row, $index }">
+                  <el-input-number v-model="row.minQty" :min="0" size="small" />
+                </template>
+              </el-table-column>
+              <el-table-column label="鏈�澶ф暟閲�" width="120">
+                <template #default="{ row, $index }">
+                  <el-input-number v-model="row.maxQty" :min="0" size="small" />
+                </template>
+              </el-table-column>
+              <el-table-column label="鎶樻墸鐜�(%)" width="120">
+                <template #default="{ row, $index }">
+                  <el-input-number v-model="row.discount" :min="0" :max="100" :precision="2" size="small" />
+                </template>
+              </el-table-column>
+              <el-table-column label="鎿嶄綔" width="80">
+                <template #default="{ row, $index }">
+                  <el-button type="danger" link @click="removeTieredRow($index)">鍒犻櫎</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+            <el-button type="primary" link @click="addTieredRow" class="mt-2">娣诲姞闃舵</el-button>
+          </el-form-item>
+        </div>
+
+        <!-- 浠锋牸鎺у埗 -->
+        <el-divider content-position="left">浠锋牸鎺у埗</el-divider>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="鏈�浣庝环鏍�">
+              <el-input-number v-model="formData.minPrice" :min="0" :precision="2" placeholder="鏈�浣庝环鏍�" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="鏈�楂樹环鏍�">
+              <el-input-number v-model="formData.maxPrice" :min="0" :precision="2" placeholder="鏈�楂樹环鏍�" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="棰勮闃堝��(%)">
+              <el-input-number v-model="formData.warningThreshold" :min="0" :max="100" :precision="1" placeholder="棰勮闃堝��" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鐢熸晥鏃堕棿" prop="effectiveTime">
+              <el-date-picker v-model="formData.effectiveTime" type="datetime" placeholder="閫夋嫨鐢熸晥鏃堕棿" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="澶辨晥鏃堕棿">
+              <el-date-picker v-model="formData.expireTime" type="datetime" placeholder="閫夋嫨澶辨晥鏃堕棿" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="璋冧环鍘熷洜" prop="reason">
+          <el-select v-model="formData.reason" placeholder="璇烽�夋嫨璋冧环鍘熷洜" style="width: 100%">
+            <el-option label="甯傚満浠锋牸鍙樺姩" value="market" />
+            <el-option label="鎴愭湰鍙樺寲" value="cost" />
+            <el-option label="渚涘簲鍟嗚皟鏁�" value="supplier" />
+            <el-option label="瀛h妭鎬ц皟鏁�" value="seasonal" />
+            <el-option label="淇冮攢娲诲姩" value="promotion" />
+            <el-option label="鍏朵粬鍘熷洜" value="other" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="澶囨敞">
+          <el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        <el-button type="primary" @click="handleSubmit" :loading="submitLoading">纭畾</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 鎵归噺鎶樻墸瀵硅瘽妗� -->
+    <el-dialog v-model="batchDiscountVisible" title="鎵归噺璁剧疆鎶樻墸" width="600px">
+      <el-form :model="batchDiscountForm" label-width="120px">
+        <el-form-item label="鎶樻墸绫诲瀷">
+          <el-select v-model="batchDiscountForm.discountType" placeholder="璇烽�夋嫨鎶樻墸绫诲瀷" style="width: 100%">
+            <el-option label="鐧惧垎姣旀姌鎵�" value="percentage" />
+            <el-option label="鍥哄畾閲戦" value="fixed" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鎶樻墸鍊�">
+          <el-input-number 
+            v-model="batchDiscountForm.discountValue" 
+            :min="0" 
+            :max="batchDiscountForm.discountType === 'percentage' ? 100 : undefined"
+            :precision="2" 
+            placeholder="璇疯緭鍏ユ姌鎵e��" 
+            style="width: 100%" 
+          />
+        </el-form-item>
+        <el-form-item label="鐢熸晥鏃堕棿">
+          <el-date-picker v-model="batchDiscountForm.effectiveTime" type="datetime" placeholder="閫夋嫨鐢熸晥鏃堕棿" style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="澶辨晥鏃堕棿">
+          <el-date-picker v-model="batchDiscountForm.expireTime" type="datetime" placeholder="閫夋嫨澶辨晥鏃堕棿" style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="閫傜敤鍟嗗搧">
+          <div class="selected-items">
+            宸查�夋嫨 {{ selectedRows.length }} 涓晢鍝�
+          </div>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="batchDiscountVisible = false">鍙栨秷</el-button>
+        <el-button type="primary" @click="handleBatchDiscount">纭畾</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 浠锋牸鎺у埗瀵硅瘽妗� -->
+    <el-dialog v-model="priceControlVisible" title="浠锋牸鎺у埗璁剧疆" width="700px">
+      <el-form :model="priceControlForm" label-width="120px">
+        <el-form-item label="榛樿鏈�浣庝环鏍�">
+          <el-input-number v-model="priceControlForm.defaultMinPrice" :min="0" :precision="2" style="width: 200px" />
+        </el-form-item>
+        <el-form-item label="榛樿鏈�楂樹环鏍�">
+          <el-input-number v-model="priceControlForm.defaultMaxPrice" :min="0" :precision="2" style="width: 200px" />
+        </el-form-item>
+        <el-form-item label="浠锋牸鍙樺姩闃堝��">
+          <el-input-number v-model="priceControlForm.changeThreshold" :min="0" :max="100" :precision="1" style="width: 200px" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="priceControlVisible = false">鍙栨秷</el-button>
+        <el-button type="primary" @click="handlePriceControl">淇濆瓨璁剧疆</el-button>
+      </template>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {
+  Search, Refresh, Plus, Discount, Setting, Download, Delete, Edit, 
+  Warning
+} from '@element-plus/icons-vue'
+
+// 鍝嶅簲寮忔暟鎹�
+const loading = ref(false)
+const submitLoading = ref(false)
+const dialogVisible = ref(false)
+const batchDiscountVisible = ref(false)
+const priceControlVisible = ref(false)
+const dialogType = ref('add')
+const selectedRows = ref([])
+const formRef = ref()
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+  productName: '',
+  supplierId: '',
+  priceStatus: ''
+})
+
+// 鍒嗛〉
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 20,
+  total: 0
+})
+
+
+// 琛ㄥ崟鏁版嵁
+const formData = reactive({
+  productName: '',
+  productCode: '',
+  specification: '',
+  supplierName: '',
+  basePrice: 0,
+  unit: '',
+  discountType: '',
+  discountValue: 0,
+  discountEndTime: '',
+  tieredDiscount: [],
+  minPrice: null,
+  maxPrice: null,
+  warningThreshold: 10,
+  effectiveTime: '',
+  expireTime: '',
+  reason: '',
+  remark: ''
+})
+
+// 鎵归噺鎶樻墸琛ㄥ崟
+const batchDiscountForm = reactive({
+  discountType: 'percentage',
+  discountValue: 0,
+  effectiveTime: '',
+  expireTime: ''
+})
+
+// 浠锋牸鎺у埗琛ㄥ崟
+const priceControlForm = reactive({
+  defaultMinPrice: 0,
+  defaultMaxPrice: 0,
+  changeThreshold: 10,
+})
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const formRules = {
+  productName: [{ required: true, message: '璇烽�夋嫨鍟嗗搧鍚嶇О', trigger: 'change' }],
+  productCode: [{ required: true, message: '璇疯緭鍏ュ晢鍝佺紪鐮�', trigger: 'blur' }],
+  supplierName: [{ required: true, message: '璇烽�夋嫨渚涘簲鍟�', trigger: 'change' }],
+  basePrice: [{ required: true, message: '璇疯緭鍏ュ熀纭�浠锋牸', trigger: 'blur' }],
+  effectiveTime: [{ required: true, message: '璇烽�夋嫨鐢熸晥鏃堕棿', trigger: 'change' }],
+  reason: [{ required: true, message: '璇烽�夋嫨璋冧环鍘熷洜', trigger: 'change' }]
+}
+
+// 妯℃嫙鏁版嵁
+const tableData = ref([
+  {
+    id: 1,
+    productName: '楂樺己搴﹁灪鏍�',
+    productCode: 'HQ001',
+    specification: 'M12脳80',
+    supplierName: '浼樿川浜旈噾渚涘簲鍟�',
+    basePrice: 2.50,
+    discountType: 'percentage',
+    discountValue: 10,
+    priceControl: { minPrice: 2.00, maxPrice: 3.00 },
+    status: 'active',
+    effectiveTime: '2025-01-01 00:00:00',
+    updateTime: '2025-09-17 10:30:00',
+    unit: '涓�',
+    reason: 'market',
+    remark: '甯傚満浠锋牸璋冩暣'
+  },
+  {
+    id: 2,
+    productName: '涓嶉攬閽㈢',
+    productCode: 'BXG002',
+    specification: '桅25脳2.0',
+    supplierName: '閽㈡潗璐告槗鍏徃',
+    basePrice: 45.80,
+    discountType: 'fixed',
+    discountValue: 5,
+    priceControl: { minPrice: 40.00, maxPrice: 50.00 },
+    status: 'pending',
+    effectiveTime: '2025-10-01 00:00:00',
+    updateTime: '2025-09-16 14:20:00',
+    unit: '绫�',
+    reason: 'cost',
+    remark: '鍘熸潗鏂欐垚鏈笂娑�'
+  }
+])
+
+const supplierList = ref([
+  { id: 1, name: '浼樿川浜旈噾渚涘簲鍟�' },
+  { id: 2, name: '閽㈡潗璐告槗鍏徃' },
+  { id: 3, name: '寤烘潗鎵瑰彂鍟�' }
+])
+
+const productList = ref([
+  { id: 1, name: '楂樺己搴﹁灪鏍�' },
+  { id: 2, name: '涓嶉攬閽㈢' },
+  { id: 3, name: '閾濆悎閲戝瀷鏉�' }
+])
+
+
+// 璁$畻灞炴��
+const finalTableData = computed(() => {
+  return tableData.value.filter(item => {
+    if (searchForm.productName && !item.productName.includes(searchForm.productName)) return false
+    if (searchForm.supplierId && item.supplierId !== searchForm.supplierId) return false
+    if (searchForm.priceStatus && item.status !== searchForm.priceStatus) return false
+    
+    return true
+  })
+})
+
+// 鏂规硶
+const calculateFinalPrice = (row) => {
+  let finalPrice = row.basePrice
+  if (row.discountType === 'percentage') {
+    finalPrice = row.basePrice * (1 - row.discountValue / 100)
+  } else if (row.discountType === 'fixed') {
+    finalPrice = row.basePrice - row.discountValue
+  }
+  return Math.max(finalPrice, 0)
+}
+
+const getDiscountTagType = (discountType) => {
+  const typeMap = {
+    percentage: 'success',
+    fixed: 'warning',
+    tiered: 'info'
+  }
+  return typeMap[discountType] || 'info'
+}
+
+const getDiscountText = (discountType) => {
+  const textMap = {
+    percentage: '鐧惧垎姣�',
+    fixed: '鍥哄畾閲戦',
+    tiered: '闃舵鎶樻墸'
+  }
+  return textMap[discountType] || '鏈煡'
+}
+
+const getStatusType = (status) => {
+  const statusMap = {
+    active: 'success',
+    pending: 'warning',
+    expired: 'info',
+    suspended: 'danger'
+  }
+  return statusMap[status] || 'info'
+}
+
+const getStatusText = (status) => {
+  const statusMap = {
+    active: '鏈夋晥',
+    pending: '寰呯敓鏁�',
+    expired: '宸茶繃鏈�',
+    suspended: '宸叉殏鍋�'
+  }
+  return statusMap[status] || '鏈煡'
+}
+
+const isPriceWarning = (row) => {
+  if (!row.priceControl) return false
+  const finalPrice = calculateFinalPrice(row)
+  return finalPrice < row.priceControl.minPrice || finalPrice > row.priceControl.maxPrice
+}
+
+
+const handleSearch = () => {
+  loading.value = true
+  // 妯℃嫙API璋冪敤
+  setTimeout(() => {
+    loading.value = false
+  }, 500)
+}
+
+const resetSearch = () => {
+  Object.assign(searchForm, {
+    productName: '',
+    supplierId: '',
+    priceStatus: ''
+  })
+  handleSearch()
+}
+
+
+const openDialog = (type, row = {}) => {
+  dialogType.value = type
+  if (type === 'edit' && row.id) {
+    Object.assign(formData, {
+      ...row,
+      minPrice: row.priceControl?.minPrice,
+      maxPrice: row.priceControl?.maxPrice,
+      tieredDiscount: row.tieredDiscount || []
+    })
+  } else {
+    resetFormData()
+  }
+  dialogVisible.value = true
+}
+
+const resetFormData = () => {
+  Object.assign(formData, {
+    productName: '',
+    productCode: '',
+    specification: '',
+    supplierName: '',
+    basePrice: 0,
+    unit: '',
+    discountType: '',
+    discountValue: 0,
+    discountEndTime: '',
+    tieredDiscount: [],
+    minPrice: null,
+    maxPrice: null,
+    warningThreshold: 10,
+    effectiveTime: '',
+    expireTime: '',
+    reason: '',
+    remark: ''
+  })
+}
+
+const addTieredRow = () => {
+  formData.tieredDiscount.push({
+    minQty: 0,
+    maxQty: 0,
+    discount: 0
+  })
+}
+
+const removeTieredRow = (index) => {
+  formData.tieredDiscount.splice(index, 1)
+}
+
+const handleSubmit = async () => {
+  if (!formRef.value) return
+  
+  try {
+    await formRef.value.validate()
+    submitLoading.value = true
+    
+    // 妯℃嫙API璋冪敤
+    setTimeout(() => {
+      if (dialogType.value === 'add') {
+        const newItem = {
+          id: Date.now(),
+          ...formData,
+          priceControl: {
+            minPrice: formData.minPrice,
+            maxPrice: formData.maxPrice
+          },
+          status: 'pending',
+          updateTime: new Date().toLocaleString()
+        }
+        tableData.value.unshift(newItem)
+        ElMessage.success('鏂板鎴愬姛')
+      } else {
+        // 缂栬緫閫昏緫
+        ElMessage.success('缂栬緫鎴愬姛')
+      }
+      
+      dialogVisible.value = false
+      submitLoading.value = false
+    }, 1000)
+  } catch (error) {
+    console.error('琛ㄥ崟楠岃瘉澶辫触:', error)
+  }
+}
+
+const openBatchDiscountDialog = () => {
+  if (selectedRows.value.length === 0) {
+    ElMessage.warning('璇峰厛閫夋嫨瑕佽缃姌鎵g殑鍟嗗搧')
+    return
+  }
+  batchDiscountVisible.value = true
+}
+
+const handleBatchDiscount = () => {
+  // 鎵归噺璁剧疆鎶樻墸閫昏緫
+  selectedRows.value.forEach(row => {
+    row.discountType = batchDiscountForm.discountType
+    row.discountValue = batchDiscountForm.discountValue
+  })
+  
+  ElMessage.success(`宸蹭负 ${selectedRows.value.length} 涓晢鍝佽缃姌鎵)
+  batchDiscountVisible.value = false
+}
+
+const openPriceControlDialog = () => {
+  priceControlVisible.value = true
+}
+
+const handlePriceControl = () => {
+  ElMessage.success('浠锋牸鎺у埗璁剧疆宸蹭繚瀛�')
+  priceControlVisible.value = false
+}
+
+const openDiscountDialog = (row) => {
+  // 鍗曚釜鍟嗗搧鎶樻墸璁剧疆
+  openDialog('edit', row)
+}
+
+
+const handleDelete = (row) => {
+  ElMessageBox.confirm('纭畾瑕佸垹闄よ繖鏉¤褰曞悧锛�', '鎻愮ず', {
+    confirmButtonText: '纭畾',
+    cancelButtonText: '鍙栨秷',
+    type: 'warning'
+  }).then(() => {
+    const index = tableData.value.findIndex(item => item.id === row.id)
+    if (index !== -1) {
+      tableData.value.splice(index, 1)
+      ElMessage.success('鍒犻櫎鎴愬姛')
+    }
+  })
+}
+
+const handleBatchDelete = () => {
+  if (selectedRows.value.length === 0) {
+    ElMessage.warning('璇峰厛閫夋嫨瑕佸垹闄ょ殑璁板綍')
+    return
+  }
+  
+  ElMessageBox.confirm(`纭畾瑕佸垹闄ら�変腑鐨� ${selectedRows.value.length} 鏉¤褰曞悧锛焋, '鎻愮ず', {
+    confirmButtonText: '纭畾',
+    cancelButtonText: '鍙栨秷',
+    type: 'warning'
+  }).then(() => {
+    selectedRows.value.forEach(row => {
+      const index = tableData.value.findIndex(item => item.id === row.id)
+      if (index !== -1) {
+        tableData.value.splice(index, 1)
+      }
+    })
+    ElMessage.success('鎵归噺鍒犻櫎鎴愬姛')
+    selectedRows.value = []
+  })
+}
+
+const handleSelectionChange = (rows) => {
+  selectedRows.value = rows
+}
+
+const handleSizeChange = (size) => {
+  pagination.pageSize = size
+  handleSearch()
+}
+
+const handleCurrentChange = (page) => {
+  pagination.currentPage = page
+  handleSearch()
+}
+
+const exportData = () => {
+  ElMessage.success('鏁版嵁瀵煎嚭鍔熻兘寮�鍙戜腑...')
+}
+
+// 鐢熷懡鍛ㄦ湡
+onMounted(() => {
+  handleSearch()
+})
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+.search-card, .action-card, .table-card {
+  margin-bottom: 20px;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 10px;
+  flex-wrap: wrap;
+}
+
+
+.product-info {
+  line-height: 1.4;
+}
+
+.product-name {
+  font-weight: bold;
+  color: #303133;
+}
+
+.product-spec, .product-code {
+  font-size: 12px;
+  color: #909399;
+}
+
+.price-text {
+  font-weight: bold;
+  color: #409EFF;
+}
+
+.final-price {
+  font-weight: bold;
+  color: #67C23A;
+  font-size: 16px;
+}
+
+.discount-value {
+  font-size: 12px;
+  color: #E6A23C;
+  margin-top: 2px;
+}
+
+.no-discount {
+  color: #C0C4CC;
+  font-size: 12px;
+}
+
+.price-control {
+  font-size: 12px;
+  line-height: 1.3;
+}
+
+.control-item {
+  color: #909399;
+}
+
+.warning-indicator {
+  margin-top: 2px;
+}
+
+.pagination-wrapper {
+  display: flex;
+  justify-content: end;
+  margin-top: 20px;
+}
+
+.selected-items {
+  color: #409EFF;
+  font-weight: bold;
+}
+
+
+.mt-2 {
+  margin-top: 8px;
+}
+
+.ml-2 {
+  margin-left: 8px;
+}
+
+:deep(.el-table) {
+  font-size: 13px;
+}
+
+:deep(.el-table th) {
+  background-color: #fafafa;
+}
+
+:deep(.el-card__body) {
+  padding: 15px;
+}
+
+:deep(.el-divider__text) {
+  font-weight: bold;
+  color: #409EFF;
+}
+</style>

--
Gitblit v1.9.3