From cb76922f5e8ccade2bdd3e1c73c64c2fe711c45f Mon Sep 17 00:00:00 2001
From: chenhj <1263187585@qq.com>
Date: 星期一, 20 四月 2026 15:46:20 +0800
Subject: [PATCH] feat(salesQuotation): 添加导入详情功能及优化界面交互

---
 src/views/salesManagement/salesQuotation/ImportQuotationDetail.vue |  105 +++++
 src/views/salesManagement/salesQuotation/index.vue                 | 1052 ++++++++++++++++++++++++++++------------------------
 src/api/salesManagement/salesQuotationRecord.js                    |   11 
 3 files changed, 678 insertions(+), 490 deletions(-)

diff --git a/src/api/salesManagement/salesQuotationRecord.js b/src/api/salesManagement/salesQuotationRecord.js
new file mode 100644
index 0000000..4276312
--- /dev/null
+++ b/src/api/salesManagement/salesQuotationRecord.js
@@ -0,0 +1,11 @@
+// 閿�鍞姤浠烽〉闈㈡帴鍙�
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ鎶ヤ环鍗曞垪琛�
+export function getQuotationRecordList(query) {
+  return request({
+    url: "/sales/quotationRecord/listPage",
+    method: "get",
+    params: query,
+  });
+}
\ No newline at end of file
diff --git a/src/views/salesManagement/salesQuotation/ImportQuotationDetail.vue b/src/views/salesManagement/salesQuotation/ImportQuotationDetail.vue
new file mode 100644
index 0000000..6371632
--- /dev/null
+++ b/src/views/salesManagement/salesQuotation/ImportQuotationDetail.vue
@@ -0,0 +1,105 @@
+<script setup>
+import {ref} from 'vue'
+import {getQuotationRecordList} from "@/api/salesManagement/salesQuotationRecord.js";
+
+const props = defineProps({
+  showModal: {
+    type: Boolean,
+    required: true
+  },
+  quotationId: {
+    type: Number,
+    required: true
+  }
+})
+
+const emit = defineEmits(['update:showModal'])
+
+const dialogVisible = computed({
+  get: () => props.showModal,
+  set: (val) => emit('update:showModal', val)
+})
+
+const leftTableData = ref([])
+const rightTableData = ref([])
+const selectedLeftRow = ref(null)
+
+const handleRowClick = (row) => {
+  selectedLeftRow.value = row
+  rightTableData.value = row.products || []
+}
+
+const tableRowClassName = ({row}) => {
+  return selectedLeftRow.value === row ? 'selected-row' : ''
+}
+
+const fetchData = () => {
+  getQuotationRecordList({quotationRecordId: props.quotationId}).then(res => {
+    const data = res.data.records || []
+    data.forEach(item => {
+      leftTableData.value.push(JSON.parse(item?.info || '{}'))
+    })
+  })
+}
+
+const leftColumns = [
+  {prop: 'customer', label: '瀹㈡埛鍚嶇О', minWidth: '120'},
+  {prop: 'salesperson', label: '涓氬姟鍛�', minWidth: '120'},
+  {prop: 'quotationDate', label: '鎶ヤ环鏃ユ湡', minWidth: '120'},
+  {prop: 'validDate', label: '鏈夋晥鏈熻嚦', minWidth: '120'},
+  {prop: 'paymentMethod', label: '鏀粯鏂瑰紡', minWidth: '120'},
+  {prop: 'remark', label: '澶囨敞', minWidth: '120'}
+]
+
+const rightColumns = [
+  {prop: 'product', label: '浜у搧鍚嶇О', minWidth: '120'},
+  {prop: 'specification', label: '鍨嬪彿', minWidth: '120'},
+  {prop: 'unit', label: '鍗曚綅', minWidth: '100'},
+  {prop: 'unitPrice', label: '鍗曚环', minWidth: '100'}
+]
+
+onMounted(() => {
+  fetchData()
+})
+
+</script>
+
+<template>
+  <el-dialog v-model="dialogVisible" title="璇︽儏" width="1000px" :close-on-click-modal="false">
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <div class="table-title">鎶ヤ环鍗曡褰�</div>
+        <el-table :data="leftTableData" border height="400" stripe highlight-current-row
+                  @row-click="handleRowClick" :row-class-name="tableRowClassName">
+          <el-table-column v-for="col in leftColumns" :key="col.prop" :prop="col.prop" :label="col.label"
+                           :min-width="col.minWidth" show-overflow-tooltip/>
+        </el-table>
+      </el-col>
+      <el-col :span="12">
+        <div class="table-title">鎶ヤ环鍗曚骇鍝�</div>
+        <el-table :data="rightTableData" border height="400" stripe>
+          <el-table-column v-for="col in rightColumns" :key="col.prop" :prop="col.prop" :label="col.label"
+                           :min-width="col.minWidth" show-overflow-tooltip/>
+        </el-table>
+      </el-col>
+    </el-row>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="dialogVisible = false">鍏抽棴</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<style scoped>
+.table-title {
+  font-size: 15px;
+  font-weight: bold;
+  margin-bottom: 10px;
+  color: #303133;
+}
+
+:deep(.selected-row) {
+  background-color: #ecf5ff !important;
+}
+</style>
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index 0acfc93..23b68be 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -5,33 +5,36 @@
       <el-row :gutter="20" class="search-row">
         <el-col :span="8">
           <el-input
-            v-model="searchForm.quotationNo"
-            placeholder="璇疯緭鍏ユ姤浠峰崟鍙�"
-            clearable
-            @keyup.enter="handleSearch"
+              v-model="searchForm.quotationNo"
+              placeholder="璇疯緭鍏ユ姤浠峰崟鍙�"
+              clearable
+              @keyup.enter="handleSearch"
           >
             <template #prefix>
-              <el-icon><Search /></el-icon>
+              <el-icon>
+                <Search/>
+              </el-icon>
             </template>
           </el-input>
         </el-col>
         <el-col :span="8">
           <el-select v-model="searchForm.customer" placeholder="璇烽�夋嫨瀹㈡埛" clearable>
-						<el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
-							{{
-								item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
-							}}
-						</el-option>
+            <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName"
+                       :value="item.customerName">
+              {{
+                item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
+              }}
+            </el-option>
           </el-select>
         </el-col>
-<!--        <el-col :span="6">-->
-<!--          <el-select v-model="searchForm.status" 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="searchForm.status" 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="8">
           <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
           <el-button @click="resetSearch">閲嶇疆</el-button>
@@ -46,19 +49,19 @@
 
       <!-- 鎶ヤ环鍒楄〃 -->
       <el-table
-        :data="filteredList"
-        style="width: 100%"
-        v-loading="loading"
-        border
-        stripe
-        height="calc(100vh - 22em)"
+          :data="filteredList"
+          style="width: 100%"
+          v-loading="loading"
+          border
+          stripe
+          height="calc(100vh - 22em)"
       >
-				<el-table-column align="center" label="搴忓彿" type="index" width="60" />
-        <el-table-column prop="quotationNo" label="鎶ヤ环鍗曞彿" />
-        <el-table-column prop="customer" label="瀹㈡埛鍚嶇О" />
-        <el-table-column prop="salesperson" label="涓氬姟鍛�" width="100" />
-        <el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" width="120" />
-        <el-table-column prop="validDate" label="鏈夋晥鏈熻嚦" width="120" />
+        <el-table-column align="center" label="搴忓彿" type="index" width="60"/>
+        <el-table-column prop="quotationNo" label="鎶ヤ环鍗曞彿"/>
+        <el-table-column prop="customer" label="瀹㈡埛鍚嶇О"/>
+        <el-table-column prop="salesperson" label="涓氬姟鍛�" width="100"/>
+        <el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" width="120"/>
+        <el-table-column prop="validDate" label="鏈夋晥鏈熻嚦" width="120"/>
         <el-table-column prop="status" label="瀹℃壒鐘舵��" width="120" align="center">
           <template #default="{ row }">
             <el-tag :type="getStatusType(row.status)" disable-transitions>
@@ -71,350 +74,384 @@
             楼{{ scope.row.totalAmount.toFixed(2) }}
           </template>
         </el-table-column>
-        <el-table-column label="鎿嶄綔" width="200" fixed="right" align="center">
+        <el-table-column label="鎿嶄綔" width="250" fixed="right" align="center">
           <template #default="scope">
-            <el-button link type="primary" @click="handleEdit(scope.row)" :disabled="!['寰呭鎵�','鎷掔粷'].includes(scope.row.status)">缂栬緫</el-button>
+            <el-button link type="primary" @click="handleEdit(scope.row)"
+                       :disabled="!['寰呭鎵�','鎷掔粷'].includes(scope.row.status)">缂栬緫
+            </el-button>
             <el-button link type="primary" @click="handleView(scope.row)" style="color: #67C23A">鏌ョ湅</el-button>
             <el-button link type="danger" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+            <el-button link type="primary" @click="handleImportDetail(scope.row)">瀵煎叆璇︽儏</el-button>
           </template>
         </el-table-column>
       </el-table>
 
       <!-- 鍒嗛〉 -->
       <pagination
-        :total="pagination.total"
-        layout="total, sizes, prev, pager, next, jumper"
-        :page="pagination.currentPage"
-        :limit="pagination.pageSize"
-        @pagination="handleCurrentChange"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          :page="pagination.currentPage"
+          :limit="pagination.pageSize"
+          @pagination="handleCurrentChange"
       />
     </el-card>
 
     <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
-    <FormDialog v-model="dialogVisible" :title="dialogTitle" width="85%" :close-on-click-modal="false" @close="dialogVisible = false" @confirm="handleSubmit" @cancel="dialogVisible = false">
+    <FormDialog v-model="dialogVisible" :title="dialogTitle" width="85%" :close-on-click-modal="false"
+                @close="dialogVisible = false" @confirm="handleSubmit" @cancel="dialogVisible = false">
       <div class="quotation-form-container">
         <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="quotation-form">
-        <!-- 鍩烘湰淇℃伅 -->
-        <el-card class="form-card" shadow="hover">
-          <template #header>
-            <div class="card-header-wrapper">
-              <el-icon class="card-icon"><Document /></el-icon>
-              <span class="card-title">鍩烘湰淇℃伅</span>
-            </div>
-          </template>
-          <div class="form-content">
-            <el-row :gutter="24">
-              <el-col :span="12">
-                <el-form-item label="瀹㈡埛鍚嶇О" prop="customer">
-                  <el-select v-model="form.customer" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%" @change="handleCustomerChange" clearable>
-                    <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
-                      {{
-                        item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
-                      }}
-                    </el-option>
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="12">
-                <el-form-item label="涓氬姟鍛�" prop="salesperson">
-                  <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%" clearable>
-                    <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
-                      :value="item.nickName" />
-                  </el-select>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="24">
-              <el-col :span="12">
-                <el-form-item label="鎶ヤ环鏃ユ湡" prop="quotationDate">
-                  <el-date-picker
-                    v-model="form.quotationDate"
-                    type="date"
-                    placeholder="閫夋嫨鎶ヤ环鏃ユ湡"
-                    style="width: 100%"
-                    format="YYYY-MM-DD"
-                    value-format="YYYY-MM-DD"
-                    clearable
-                  />
-                </el-form-item>
-              </el-col>
-              <el-col :span="12">
-                <el-form-item label="鏈夋晥鏈熻嚦" prop="validDate">
-                  <el-date-picker
-                    v-model="form.validDate"
-                    type="date"
-                    placeholder="閫夋嫨鏈夋晥鏈�"
-                    style="width: 100%"
-                    format="YYYY-MM-DD"
-                    value-format="YYYY-MM-DD"
-                    clearable
-                  />
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="24">
-              <el-col :span="12">
-                <el-form-item label="浠樻鏂瑰紡" prop="paymentMethod">
-                  <el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏ヤ粯娆炬柟寮�" clearable />
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </div>
-        </el-card>
-
-        <!-- 瀹℃壒浜轰俊鎭� -->
-        <el-card class="form-card" shadow="hover">
-          <template #header>
-            <div class="card-header-wrapper">
-              <el-icon class="card-icon"><UserFilled /></el-icon>
-              <span class="card-title">瀹℃壒浜洪�夋嫨</span>
-              <el-button type="primary" size="small" @click="addApproverNode" class="header-btn">
-                <el-icon><Plus /></el-icon>
-                鏂板鑺傜偣
-              </el-button>
-            </div>
-          </template>
-          <div class="form-content">
-            <el-row>
-              <el-col :span="24">
-                <el-form-item>
-                  <div class="approver-nodes-container">
-                    <div
-                      v-for="(node, index) in approverNodes"
-                      :key="node.id"
-                      class="approver-node-item"
-                    >
-                      <div class="approver-node-label">
-                        <span class="node-step">{{ index + 1 }}</span>
-                        <span class="node-text">瀹℃壒浜�</span>
-                        <el-icon class="arrow-icon"><ArrowRight /></el-icon>
-                      </div>
-                      <el-select
-                        v-model="node.userId"
-                        placeholder="閫夋嫨浜哄憳"
-                        class="approver-select"
+          <!-- 鍩烘湰淇℃伅 -->
+          <el-card class="form-card" shadow="hover">
+            <template #header>
+              <div class="card-header-wrapper">
+                <el-icon class="card-icon">
+                  <Document/>
+                </el-icon>
+                <span class="card-title">鍩烘湰淇℃伅</span>
+              </div>
+            </template>
+            <div class="form-content">
+              <el-row :gutter="24">
+                <el-col :span="12">
+                  <el-form-item label="瀹㈡埛鍚嶇О" prop="customer">
+                    <el-select v-model="form.customer" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%"
+                               @change="handleCustomerChange" clearable>
+                      <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName"
+                                 :value="item.customerName">
+                        {{
+                          item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
+                        }}
+                      </el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="涓氬姟鍛�" prop="salesperson">
+                    <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%" clearable>
+                      <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
+                                 :value="item.nickName"/>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+              </el-row>
+              <el-row :gutter="24">
+                <el-col :span="12">
+                  <el-form-item label="鎶ヤ环鏃ユ湡" prop="quotationDate">
+                    <el-date-picker
+                        v-model="form.quotationDate"
+                        type="date"
+                        placeholder="閫夋嫨鎶ヤ环鏃ユ湡"
+                        style="width: 100%"
+                        format="YYYY-MM-DD"
+                        value-format="YYYY-MM-DD"
                         clearable
+                    />
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="鏈夋晥鏈熻嚦" prop="validDate">
+                    <el-date-picker
+                        v-model="form.validDate"
+                        type="date"
+                        placeholder="閫夋嫨鏈夋晥鏈�"
+                        style="width: 100%"
+                        format="YYYY-MM-DD"
+                        value-format="YYYY-MM-DD"
+                        clearable
+                    />
+                  </el-form-item>
+                </el-col>
+              </el-row>
+              <el-row :gutter="24">
+                <el-col :span="12">
+                  <el-form-item label="浠樻鏂瑰紡" prop="paymentMethod">
+                    <el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏ヤ粯娆炬柟寮�" clearable/>
+                  </el-form-item>
+                </el-col>
+              </el-row>
+            </div>
+          </el-card>
+
+          <!-- 瀹℃壒浜轰俊鎭� -->
+          <el-card class="form-card" shadow="hover">
+            <template #header>
+              <div class="card-header-wrapper">
+                <el-icon class="card-icon">
+                  <UserFilled/>
+                </el-icon>
+                <span class="card-title">瀹℃壒浜洪�夋嫨</span>
+                <el-button type="primary" size="small" @click="addApproverNode" class="header-btn">
+                  <el-icon>
+                    <Plus/>
+                  </el-icon>
+                  鏂板鑺傜偣
+                </el-button>
+              </div>
+            </template>
+            <div class="form-content">
+              <el-row>
+                <el-col :span="24">
+                  <el-form-item>
+                    <div class="approver-nodes-container">
+                      <div
+                          v-for="(node, index) in approverNodes"
+                          :key="node.id"
+                          class="approver-node-item"
+                      >
+                        <div class="approver-node-label">
+                          <span class="node-step">{{ index + 1 }}</span>
+                          <span class="node-text">瀹℃壒浜�</span>
+                          <el-icon class="arrow-icon">
+                            <ArrowRight/>
+                          </el-icon>
+                        </div>
+                        <el-select
+                            v-model="node.userId"
+                            placeholder="閫夋嫨浜哄憳"
+                            class="approver-select"
+                            clearable
+                        >
+                          <el-option
+                              v-for="user in userList"
+                              :key="user.userId"
+                              :label="user.nickName"
+                              :value="user.userId"
+                          />
+                        </el-select>
+                        <el-button
+                            type="danger"
+                            size="small"
+                            :icon="Delete"
+                            @click="removeApproverNode(index)"
+                            v-if="approverNodes.length > 1"
+                            class="remove-btn"
+                        >鍒犻櫎
+                        </el-button>
+                      </div>
+                    </div>
+                  </el-form-item>
+                </el-col>
+              </el-row>
+            </div>
+          </el-card>
+
+          <!-- 浜у搧淇℃伅 -->
+          <el-card class="form-card" shadow="hover">
+            <template #header>
+              <div class="card-header-wrapper">
+                <el-icon class="card-icon">
+                  <Box/>
+                </el-icon>
+                <span class="card-title">浜у搧淇℃伅</span>
+                <el-button type="primary" size="small" @click="addProduct" class="header-btn">
+                  <el-icon>
+                    <Plus/>
+                  </el-icon>
+                  娣诲姞浜у搧
+                </el-button>
+              </div>
+            </template>
+            <div class="form-content">
+              <el-table :data="form.products" border style="width: 100%" class="product-table"
+                        v-if="form.products.length > 0">
+                <el-table-column prop="product" label="浜у搧鍚嶇О" width="200">
+                  <template #default="scope">
+                    <el-form-item :prop="`products.${scope.$index}.productId`" class="product-table-form-item">
+                      <el-tree-select
+                          v-model="scope.row.productId"
+                          placeholder="璇烽�夋嫨"
+                          clearable
+                          check-strictly
+                          @change="getModels($event, scope.row)"
+                          :data="productOptions"
+                          :render-after-expand="false"
+                          style="width: 100%"
+                      />
+                    </el-form-item>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" width="200">
+                  <template #default="scope">
+                    <el-form-item :prop="`products.${scope.$index}.specificationId`" class="product-table-form-item">
+                      <el-select
+                          v-model="scope.row.specificationId"
+                          placeholder="璇烽�夋嫨"
+                          clearable
+                          @change="getProductModel($event, scope.row)"
+                          style="width: 100%"
                       >
                         <el-option
-                          v-for="user in userList"
-                          :key="user.userId"
-                          :label="user.nickName"
-                          :value="user.userId"
+                            v-for="item in scope.row.modelOptions || []"
+                            :key="item.id"
+                            :label="item.model"
+                            :value="item.id"
                         />
                       </el-select>
-                      <el-button
-                        type="danger"
-                        size="small"
-                        :icon="Delete"
-                        @click="removeApproverNode(index)"
-                        v-if="approverNodes.length > 1"
-                        class="remove-btn"
-                      >鍒犻櫎</el-button>
-                    </div>
-                  </div>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </div>
-        </el-card>
-
-        <!-- 浜у搧淇℃伅 -->
-        <el-card class="form-card" shadow="hover">
-          <template #header>
-            <div class="card-header-wrapper">
-              <el-icon class="card-icon"><Box /></el-icon>
-              <span class="card-title">浜у搧淇℃伅</span>
-              <el-button type="primary" size="small" @click="addProduct" class="header-btn">
-                <el-icon><Plus /></el-icon>
-                娣诲姞浜у搧
-              </el-button>
+                    </el-form-item>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="unit" label="鍗曚綅">
+                  <template #default="scope">
+                    <el-form-item :prop="`products.${scope.$index}.unit`" class="product-table-form-item">
+                      <el-input v-model="scope.row.unit" placeholder="鍗曚綅" clearable/>
+                    </el-form-item>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="unitPrice" label="鍗曚环">
+                  <template #default="scope">
+                    <el-form-item :prop="`products.${scope.$index}.unitPrice`" class="product-table-form-item">
+                      <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%"/>
+                    </el-form-item>
+                  </template>
+                </el-table-column>
+                <el-table-column label="鎿嶄綔" width="80" align="center">
+                  <template #default="scope">
+                    <el-button link type="danger" @click="removeProduct(scope.$index)">鍒犻櫎</el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <el-empty v-else description="鏆傛棤浜у搧锛岃鐐瑰嚮娣诲姞浜у搧" :image-size="80"/>
             </div>
-          </template>
-          <div class="form-content">
-            <el-table :data="form.products" border style="width: 100%" class="product-table" v-if="form.products.length > 0">
-            <el-table-column prop="product" label="浜у搧鍚嶇О" width="200">
-              <template #default="scope">
-                <el-form-item :prop="`products.${scope.$index}.productId`" class="product-table-form-item">
-                  <el-tree-select
-                    v-model="scope.row.productId"
-                    placeholder="璇烽�夋嫨"
-                    clearable
-                    check-strictly
-                    @change="getModels($event, scope.row)"
-                    :data="productOptions"
-                    :render-after-expand="false"
-                    style="width: 100%"
-                  />
-                </el-form-item>
-              </template>
-            </el-table-column>
-            <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" width="200">
-              <template #default="scope">
-                <el-form-item :prop="`products.${scope.$index}.specificationId`" class="product-table-form-item">
-                  <el-select
-                    v-model="scope.row.specificationId"
-                    placeholder="璇烽�夋嫨"
-                    clearable
-                    @change="getProductModel($event, scope.row)"
-                    style="width: 100%"
-                  >
-                    <el-option
-                      v-for="item in scope.row.modelOptions || []"
-                      :key="item.id"
-                      :label="item.model"
-                      :value="item.id"
-                    />
-                  </el-select>
-                </el-form-item>
-              </template>
-            </el-table-column>
-            <el-table-column prop="unit" label="鍗曚綅">
-              <template #default="scope">
-                <el-form-item :prop="`products.${scope.$index}.unit`" class="product-table-form-item">
-                  <el-input v-model="scope.row.unit" placeholder="鍗曚綅" clearable/>
-                </el-form-item>
-              </template>
-            </el-table-column>
-            <el-table-column prop="unitPrice" label="鍗曚环">
-              <template #default="scope">
-                <el-form-item :prop="`products.${scope.$index}.unitPrice`" class="product-table-form-item">
-                  <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
-                </el-form-item>
-              </template>
-            </el-table-column>
-            <el-table-column label="鎿嶄綔" width="80" align="center">
-              <template #default="scope">
-                <el-button link type="danger" @click="removeProduct(scope.$index)">鍒犻櫎</el-button>
-              </template>
-            </el-table-column>
-          </el-table>
-          <el-empty v-else description="鏆傛棤浜у搧锛岃鐐瑰嚮娣诲姞浜у搧" :image-size="80" />
-          </div>
-        </el-card>
+          </el-card>
 
-        <!-- 澶囨敞淇℃伅 -->
-        <el-card class="form-card" shadow="hover">
-          <template #header>
-            <div class="card-header-wrapper">
-              <el-icon class="card-icon"><EditPen /></el-icon>
-              <span class="card-title">澶囨敞淇℃伅</span>
+          <!-- 澶囨敞淇℃伅 -->
+          <el-card class="form-card" shadow="hover">
+            <template #header>
+              <div class="card-header-wrapper">
+                <el-icon class="card-icon">
+                  <EditPen/>
+                </el-icon>
+                <span class="card-title">澶囨敞淇℃伅</span>
+              </div>
+            </template>
+            <div class="form-content">
+              <el-form-item label="澶囨敞" prop="remark">
+                <el-input
+                    type="textarea"
+                    v-model="form.remark"
+                    placeholder="璇疯緭鍏ュ娉ㄤ俊鎭紙閫夊~锛�"
+                    :rows="4"
+                    maxlength="500"
+                    show-word-limit
+                ></el-input>
+              </el-form-item>
             </div>
-          </template>
-          <div class="form-content">
-            <el-form-item label="澶囨敞" prop="remark">
-              <el-input 
-                type="textarea" 
-                v-model="form.remark" 
-                placeholder="璇疯緭鍏ュ娉ㄤ俊鎭紙閫夊~锛�" 
-                :rows="4"
-                maxlength="500"
-                show-word-limit
-              ></el-input>
-            </el-form-item>
-          </div>
-        </el-card>
-      </el-form>
+          </el-card>
+        </el-form>
       </div>
     </FormDialog>
 
-    <FormDialog v-model="importDialogVisible" title="瀵煎叆鎶ヤ环鍗�" width="85%" :close-on-click-modal="false" @close="importDialogVisible = false" @confirm="handleImportSubmit" @cancel="importDialogVisible = false">
+    <FormDialog v-model="importDialogVisible" title="瀵煎叆鎶ヤ环鍗�" width="85%" :close-on-click-modal="false"
+                @close="importDialogVisible = false" @confirm="handleImportSubmit"
+                @cancel="importDialogVisible = false">
       <!-- 瀹℃壒浜轰俊鎭� -->
-        <el-card class="form-card" shadow="hover">
-          <template #header>
-            <div class="card-header-wrapper">
-              <el-icon class="card-icon"><UserFilled /></el-icon>
-              <span class="card-title">瀹℃壒浜洪�夋嫨</span>
-              <el-button type="primary" size="small" @click="addImportApproverNode" class="header-btn">
-                <el-icon><Plus /></el-icon>
-                鏂板鑺傜偣
-              </el-button>
-            </div>
-          </template>
-          <div class="form-content">
-            <el-row>
-              <el-col :span="24">
-                <el-form-item>
-                  <div class="approver-nodes-container">
-                    <div
+      <el-card class="form-card" shadow="hover">
+        <template #header>
+          <div class="card-header-wrapper">
+            <el-icon class="card-icon">
+              <UserFilled/>
+            </el-icon>
+            <span class="card-title">瀹℃壒浜洪�夋嫨</span>
+            <el-button type="primary" size="small" @click="addImportApproverNode" class="header-btn">
+              <el-icon>
+                <Plus/>
+              </el-icon>
+              鏂板鑺傜偣
+            </el-button>
+          </div>
+        </template>
+        <div class="form-content">
+          <el-row>
+            <el-col :span="24">
+              <el-form-item>
+                <div class="approver-nodes-container">
+                  <div
                       v-for="(node, index) in importApproverNodes"
                       :key="node.id"
                       class="approver-node-item"
-                    >
-                      <div class="approver-node-label">
-                        <span class="node-step">{{ index + 1 }}</span>
-                        <span class="node-text">瀹℃壒浜�</span>
-                        <el-icon class="arrow-icon"><ArrowRight /></el-icon>
-                      </div>
-                      <el-select
+                  >
+                    <div class="approver-node-label">
+                      <span class="node-step">{{ index + 1 }}</span>
+                      <span class="node-text">瀹℃壒浜�</span>
+                      <el-icon class="arrow-icon">
+                        <ArrowRight/>
+                      </el-icon>
+                    </div>
+                    <el-select
                         v-model="node.userId"
                         placeholder="閫夋嫨浜哄憳"
                         class="approver-select"
                         clearable
-                      >
-                        <el-option
+                    >
+                      <el-option
                           v-for="user in userList"
                           :key="user.userId"
                           :label="user.nickName"
                           :value="user.userId"
-                        />
-                      </el-select>
-                      <el-button
+                      />
+                    </el-select>
+                    <el-button
                         type="danger"
                         size="small"
                         :icon="Delete"
                         @click="removeImportApproverNode(index)"
                         v-if="importApproverNodes.length > 1"
                         class="remove-btn"
-                      >鍒犻櫎</el-button>
-                    </div>
+                    >鍒犻櫎
+                    </el-button>
                   </div>
-                </el-form-item>
-              </el-col>
-            </el-row>
+                </div>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+      </el-card>
+      <el-card class="form-card" shadow="hover">
+        <template #header>
+          <div class="card-header-wrapper">
+            <el-icon class="card-icon">
+              <Paperclip/>
+            </el-icon>
+            <span class="card-title">闄勪欢鏉愭枡</span>
           </div>
-        </el-card>
-        <el-card class="form-card" shadow="hover">
-          <template #header>
-            <div class="card-header-wrapper">
-              <el-icon class="card-icon"><Paperclip /></el-icon>
-              <span class="card-title">闄勪欢鏉愭枡</span>
-            </div>
-          </template>
-          <div class="form-content">
-            <el-form-item label="闄勪欢鏉愭枡" prop="files">
-          <el-upload
-            v-model:file-list="importFileList"
-            :limit="1"
-            ref="fileUpload"
-            :auto-upload="false"
-            :on-change="handleFileChange"
-            :on-exceed="handleExceed"
-            :on-remove="handleRemove"
-            :on-preview="handlePreview"
-            :show-file-list="true"
-          >
-            <el-button type="primary">涓婁紶</el-button>
-            <template #file="{ file }">
-              <div style="display:flex; align-items:center; gap: 10px; width: 100%;">
+        </template>
+        <div class="form-content">
+          <el-button type="primary" @click="downloadImportTemplate" style="margin-bottom: 20px">涓嬭浇妯℃澘鏂囦欢</el-button>
+          <el-form-item label="闄勪欢鏉愭枡" prop="files">
+            <el-upload
+                v-model:file-list="importFileList"
+                :limit="1"
+                ref="fileUpload"
+                :auto-upload="false"
+                :on-change="handleFileChange"
+                :on-exceed="handleExceed"
+                :on-remove="handleRemove"
+                :on-preview="handlePreview"
+                :show-file-list="true"
+            >
+              <el-button type="primary">涓婁紶</el-button>
+              <template #file="{ file }">
+                <div style="display:flex; align-items:center; gap: 10px; width: 100%;">
                 <span style="flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                   {{ file.name }}
                 </span>
-<!--                <div style="display:flex; align-items:center; gap: 6px;">-->
-<!--                  <el-button link type="success" :icon="Download" @click="handleDownload(file)" />-->
-<!--                  <el-button link type="primary" :icon="View" @click="handlePreview(file)" />-->
-<!--                  <el-button link type="danger" :icon="Delete" @click="triggerRemoveFile(file)" />-->
-<!--                </div>-->
-              </div>
-            </template>
-            <template #tip>
-              <div class="el-upload__tip">
-                鏀寔鏂囨。锛坸ls, xlsx锛夋牸寮�
-              </div>
-            </template>
-          </el-upload>
-        </el-form-item>
-          </div>
-        </el-card>
+                  <div style="display:flex; align-items:center; gap: 6px;">
+                    <!--                  <el-button link type="success" :icon="Download" @click="handleDownload(file)" />-->
+                    <!--                  <el-button link type="primary" :icon="View" @click="handlePreview(file)" />-->
+                    <!--                  <el-button link type="danger" :icon="Delete" @click="triggerRemoveFile(file)" />-->
+                  </div>
+                </div>
+              </template>
+              <template #tip>
+                <div class="el-upload__tip">
+                  鏀寔鏂囨。锛坸ls, xlsx锛夋牸寮�
+                </div>
+              </template>
+            </el-upload>
+          </el-form-item>
+        </div>
+      </el-card>
     </FormDialog>
 
     <!-- 鏌ョ湅璇︽儏瀵硅瘽妗� -->
@@ -426,20 +463,22 @@
         <el-descriptions-item label="鎶ヤ环鏃ユ湡">{{ currentQuotation.quotationDate }}</el-descriptions-item>
         <el-descriptions-item label="鏈夋晥鏈熻嚦">{{ currentQuotation.validDate }}</el-descriptions-item>
         <el-descriptions-item label="浠樻鏂瑰紡">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
-<!--        <el-descriptions-item label="鎶ヤ环鐘舵��">-->
-<!--          <el-tag :type="getStatusType(currentQuotation.status)">{{ currentQuotation.status }}</el-tag>-->
-<!--        </el-descriptions-item>-->
+        <!--        <el-descriptions-item label="鎶ヤ环鐘舵��">-->
+        <!--          <el-tag :type="getStatusType(currentQuotation.status)">{{ currentQuotation.status }}</el-tag>-->
+        <!--        </el-descriptions-item>-->
         <el-descriptions-item label="鎶ヤ环鎬婚" :span="2">
-          <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">楼{{ currentQuotation.totalAmount?.toFixed(2) }}</span>
+          <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">楼{{
+              currentQuotation.totalAmount?.toFixed(2)
+            }}</span>
         </el-descriptions-item>
       </el-descriptions>
-      
+
       <div style="margin: 20px 0;">
         <h4>浜у搧鏄庣粏</h4>
         <el-table :data="currentQuotation.products" border style="width: 100%">
-          <el-table-column prop="product" label="浜у搧鍚嶇О" />
-          <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" />
-          <el-table-column prop="unit" label="鍗曚綅" />
+          <el-table-column prop="product" label="浜у搧鍚嶇О"/>
+          <el-table-column prop="specification" label="瑙勬牸鍨嬪彿"/>
+          <el-table-column prop="unit" label="鍗曚綅"/>
           <el-table-column prop="unitPrice" label="鍗曚环">
             <template #default="scope">
               楼{{ scope.row.unitPrice.toFixed(2) }}
@@ -453,21 +492,38 @@
         <p>{{ currentQuotation.remark }}</p>
       </div>
     </el-dialog>
+    <ImportQuotationDetail v-if="showDetail" v-model:showModal="showDetail" :quotationId="currentQuotation.id" />
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, computed, onMounted, markRaw, shallowRef, getCurrentInstance } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete, Download, View } from '@element-plus/icons-vue'
+import {ref, reactive, computed, onMounted, markRaw, shallowRef, getCurrentInstance} from 'vue'
+import {ElMessage, ElMessageBox} from 'element-plus'
+import {
+  Search,
+  Document,
+  UserFilled,
+  Box,
+  EditPen,
+  Plus,
+  ArrowRight,
+  Delete,
+} from '@element-plus/icons-vue'
 import Pagination from '@/components/PIMTable/Pagination.vue'
 import FormDialog from '@/components/Dialog/FormDialog.vue'
-import {getQuotationList,addQuotation,updateQuotation,deleteQuotation, importQuotation} from '@/api/salesManagement/salesQuotation.js'
+import ImportQuotationDetail from '@/views/salesManagement/salesQuotation/ImportQuotationDetail.vue'
+import {
+  getQuotationList,
+  addQuotation,
+  updateQuotation,
+  deleteQuotation,
+  importQuotation
+} from '@/api/salesManagement/salesQuotation.js'
 import {userListNoPage} from "@/api/system/user.js";
 import {customerList} from "@/api/salesManagement/salesLedger.js";
 import {modelList, productTreeList} from "@/api/basicData/product.js";
 
-const { proxy } = getCurrentInstance();
+const {proxy} = getCurrentInstance();
 
 // 鍝嶅簲寮忔暟鎹�
 const loading = ref(false)
@@ -491,7 +547,7 @@
 const viewDialogVisible = ref(false)
 const importFileList = ref([])
 const importApproverNodes = ref([
-  { id: 1, userId: null }
+  {id: 1, userId: null}
 ])
 let nextImportApproverId = 2
 
@@ -517,21 +573,21 @@
 })
 
 const baseRules = {
-  customer: [{ required: true, message: '璇烽�夋嫨瀹㈡埛', trigger: 'change' }],
-  salesperson: [{ required: true, message: '璇烽�夋嫨涓氬姟鍛�', trigger: 'change' }],
-  quotationDate: [{ required: true, message: '璇烽�夋嫨鎶ヤ环鏃ユ湡', trigger: 'change' }],
-  validDate: [{ required: true, message: '璇烽�夋嫨鏈夋晥鏈�', trigger: 'change' }],
-  paymentMethod: [{ required: true, message: '璇疯緭鍏ヤ粯娆炬柟寮�', trigger: 'blur' }]
+  customer: [{required: true, message: '璇烽�夋嫨瀹㈡埛', trigger: 'change'}],
+  salesperson: [{required: true, message: '璇烽�夋嫨涓氬姟鍛�', trigger: 'change'}],
+  quotationDate: [{required: true, message: '璇烽�夋嫨鎶ヤ环鏃ユ湡', trigger: 'change'}],
+  validDate: [{required: true, message: '璇烽�夋嫨鏈夋晥鏈�', trigger: 'change'}],
+  paymentMethod: [{required: true, message: '璇疯緭鍏ヤ粯娆炬柟寮�', trigger: 'blur'}]
 }
 
 const productRowRules = {
-  productId: [{ required: true, message: '璇烽�夋嫨浜у搧鍚嶇О', trigger: 'change' }],
-  specificationId: [{ required: true, message: '璇烽�夋嫨瑙勬牸鍨嬪彿', trigger: 'change' }],
-  unit: [{ required: true, message: '璇峰~鍐欏崟浣�', trigger: 'blur' }],
-  unitPrice: [{ required: true, message: '璇峰~鍐欏崟浠�', trigger: 'change' }]
+  productId: [{required: true, message: '璇烽�夋嫨浜у搧鍚嶇О', trigger: 'change'}],
+  specificationId: [{required: true, message: '璇烽�夋嫨瑙勬牸鍨嬪彿', trigger: 'change'}],
+  unit: [{required: true, message: '璇峰~鍐欏崟浣�', trigger: 'blur'}],
+  unitPrice: [{required: true, message: '璇峰~鍐欏崟浠�', trigger: 'change'}]
 }
 const rules = computed(() => {
-  const r = { ...baseRules }
+  const r = {...baseRules}
   ;(form.products || []).forEach((_, i) => {
     r[`products.${i}.productId`] = productRowRules.productId
     r[`products.${i}.specificationId`] = productRowRules.specificationId
@@ -545,18 +601,19 @@
 
 // 瀹℃壒浜鸿妭鐐圭浉鍏�
 const approverNodes = ref([
-  { id: 1, userId: null }
+  {id: 1, userId: null}
 ])
 let nextApproverId = 2
 
 const isEdit = ref(false)
+const showDetail = ref(false)
 const editId = ref(null)
 const currentQuotation = ref({})
 const formRef = ref()
 
 // 娣诲姞瀹℃壒浜鸿妭鐐�
 function addApproverNode() {
-  approverNodes.value.push({ id: nextApproverId++, userId: null })
+  approverNodes.value.push({id: nextApproverId++, userId: null})
 }
 
 // 鍒犻櫎瀹℃壒浜鸿妭鐐�
@@ -566,7 +623,7 @@
 
 // 瀵煎叆寮圭獥瀹℃壒浜鸿妭鐐圭浉鍏�
 function addImportApproverNode() {
-  importApproverNodes.value.push({ id: nextImportApproverId++, userId: null })
+  importApproverNodes.value.push({id: nextImportApproverId++, userId: null})
 }
 
 function removeImportApproverNode(index) {
@@ -600,7 +657,7 @@
 
   const formData = new FormData()
   formData.append('file', rawFile)
-  
+
   // 瀹℃牳浜� IDs锛屼互閫楀彿鍒嗗壊
   const approveUserIds = importApproverNodes.value.map(node => node.userId).join(',')
   formData.append('approveUserIdsJson', approveUserIds)
@@ -659,7 +716,7 @@
   importFileList.value = list
 };
 
-  // 澶勭悊鏂囦欢绉婚櫎
+// 澶勭悊鏂囦欢绉婚櫎
 function triggerRemoveFile(file) {
   fileUpload.value?.handleRemove?.(file) || proxy.$refs.fileUpload?.handleRemove?.(file);
 }
@@ -711,7 +768,7 @@
   importFileList.value = []
 
   // 鉁� 娓呯┖鈥滃鍏ョ敤鈥濈殑瀹℃壒浜�
-  importApproverNodes.value = [{ id: 1, userId: null }]
+  importApproverNodes.value = [{id: 1, userId: null}]
   nextImportApproverId = 2
 
   let userLists = await userListNoPage();
@@ -729,118 +786,121 @@
   isEdit.value = false
   resetForm()
   // 閲嶇疆瀹℃壒浜鸿妭鐐�
-  approverNodes.value = [{ id: 1, userId: null }]
+  approverNodes.value = [{id: 1, userId: null}]
   nextApproverId = 2
   dialogVisible.value = true
-	let userLists = await userListNoPage();
-	// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
-	userList.value = (userLists.data || []).map(item => ({
+  let userLists = await userListNoPage();
+  // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
+  userList.value = (userLists.data || []).map(item => ({
     userId: item.userId,
     nickName: item.nickName || '',
     userName: item.userName || ''
   }));
-	getProductOptions();
-	customerList().then((res) => {
-		// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
-		customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
+  getProductOptions();
+  customerList().then((res) => {
+    // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
+    customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
       id: item.id,
       customerName: item.customerName || '',
       taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
     }))
-	});
+  });
 }
 const getProductOptions = () => {
-	// 杩斿洖 Promise锛屼究浜庣紪杈戞椂 await 纭繚鑳藉弽鏄�
-	return productTreeList().then((res) => {
-		productOptions.value = convertIdToValue(res);
-		return productOptions.value
-	});
+  // 杩斿洖 Promise锛屼究浜庣紪杈戞椂 await 纭繚鑳藉弽鏄�
+  return productTreeList().then((res) => {
+    productOptions.value = convertIdToValue(res);
+    return productOptions.value
+  });
 };
+
 function convertIdToValue(data) {
-	return data.map((item) => {
-		const { id, children, ...rest } = item;
-		const newItem = {
-			...rest,
-			value: id, // 灏� id 鏀逛负 value
-		};
-		if (children && children.length > 0) {
-			newItem.children = convertIdToValue(children);
-		}
-		
-		return newItem;
-	});
+  return data.map((item) => {
+    const {id, children, ...rest} = item;
+    const newItem = {
+      ...rest,
+      value: id, // 灏� id 鏀逛负 value
+    };
+    if (children && children.length > 0) {
+      newItem.children = convertIdToValue(children);
+    }
+
+    return newItem;
+  });
 }
+
 // 鏍规嵁鍚嶇О鍙嶆煡鑺傜偣 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
 function findNodeIdByLabel(nodes, label) {
-	if (!label) return null;
-	for (let i = 0; i < nodes.length; i++) {
-		const node = nodes[i];
-		if (node.label === label) return node.value;
-		if (node.children && node.children.length > 0) {
-			const found = findNodeIdByLabel(node.children, label);
-			if (found !== null && found !== undefined) return found;
-		}
-	}
-	return null;
+  if (!label) return null;
+  for (let i = 0; i < nodes.length; i++) {
+    const node = nodes[i];
+    if (node.label === label) return node.value;
+    if (node.children && node.children.length > 0) {
+      const found = findNodeIdByLabel(node.children, label);
+      if (found !== null && found !== undefined) return found;
+    }
+  }
+  return null;
 }
+
 const getModels = (value, row) => {
-	if (!row) return;
-	// 濡傛灉娓呯┖閫夋嫨锛屽垯娓呯┖鐩稿叧瀛楁
-	if (!value) {
-		row.productId = '';
-		row.product = '';
-		row.modelOptions = [];
-		row.specificationId = '';
-		row.specification = '';
-		row.unit = '';
-		return;
-	}
-	// 鏇存柊 productId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
-	row.productId = value;
-	// 鎵惧埌瀵瑰簲鐨� label 骞惰祴鍊肩粰 row.product
-	const label = findNodeById(productOptions.value, value);
-	if (label) {
-		row.product = label;
-	}
-	// 鑾峰彇瑙勬牸鍨嬪彿鍒楄〃锛岃缃埌褰撳墠琛岀殑 modelOptions
-	modelList({ id: value }).then((res) => {
-		row.modelOptions = res || [];
-	});
+  if (!row) return;
+  // 濡傛灉娓呯┖閫夋嫨锛屽垯娓呯┖鐩稿叧瀛楁
+  if (!value) {
+    row.productId = '';
+    row.product = '';
+    row.modelOptions = [];
+    row.specificationId = '';
+    row.specification = '';
+    row.unit = '';
+    return;
+  }
+  // 鏇存柊 productId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
+  row.productId = value;
+  // 鎵惧埌瀵瑰簲鐨� label 骞惰祴鍊肩粰 row.product
+  const label = findNodeById(productOptions.value, value);
+  if (label) {
+    row.product = label;
+  }
+  // 鑾峰彇瑙勬牸鍨嬪彿鍒楄〃锛岃缃埌褰撳墠琛岀殑 modelOptions
+  modelList({id: value}).then((res) => {
+    row.modelOptions = res || [];
+  });
 };
 const getProductModel = (value, row) => {
-	if (!row) return;
-	// 濡傛灉娓呯┖閫夋嫨锛屽垯娓呯┖鐩稿叧瀛楁
-	if (!value) {
-		row.specificationId = '';
-		row.specification = '';
-		row.unit = '';
-		return;
-	}
-	// 鏇存柊 specificationId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
-	row.specificationId = value;
-	const modelOptions = row.modelOptions || [];
-	const index = modelOptions.findIndex((item) => item.id === value);
-	if (index !== -1) {
-		row.specification = modelOptions[index].model;
-		row.unit = modelOptions[index].unit;
-	} else {
-		row.specification = '';
-		row.unit = '';
-	}
+  if (!row) return;
+  // 濡傛灉娓呯┖閫夋嫨锛屽垯娓呯┖鐩稿叧瀛楁
+  if (!value) {
+    row.specificationId = '';
+    row.specification = '';
+    row.unit = '';
+    return;
+  }
+  // 鏇存柊 specificationId锛坴-model 宸茬粡鑷姩鏇存柊锛岃繖閲岀‘淇濅竴鑷存�э級
+  row.specificationId = value;
+  const modelOptions = row.modelOptions || [];
+  const index = modelOptions.findIndex((item) => item.id === value);
+  if (index !== -1) {
+    row.specification = modelOptions[index].model;
+    row.unit = modelOptions[index].unit;
+  } else {
+    row.specification = '';
+    row.unit = '';
+  }
 };
 const findNodeById = (nodes, productId) => {
-	for (let i = 0; i < nodes.length; i++) {
-		if (nodes[i].value === productId) {
-			return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥� label
-		}
-		if (nodes[i].children && nodes[i].children.length > 0) {
-			const foundLabel = findNodeById(nodes[i].children, productId);
-			if (foundLabel) {
-				return foundLabel; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖 label
-			}
-		}
-	}
-	return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
+  for (let i = 0; i < nodes.length; i++) {
+    if (nodes[i].value === productId) {
+      return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥� label
+    }
+    if (nodes[i].children && nodes[i].children.length > 0) {
+      const foundLabel = findNodeById(nodes[i].children, productId);
+      if (foundLabel) {
+        return foundLabel; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖 label
+      }
+    }
+  }
+  return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
 };
 const handleView = (row) => {
   // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
@@ -889,18 +949,18 @@
     const productName = product.product || product.productName || ''
     // 浼樺厛鐢� productId锛涘鏋滃彧鏈夊悕绉帮紝灏濊瘯鍙嶆煡 id 浠ヤ究鏍戦�夋嫨鍣ㄥ弽鏄�
     const resolvedProductId = product.productId
-      ? Number(product.productId)
-      : findNodeIdByLabel(productOptions.value, productName) || ''
-    
+        ? Number(product.productId)
+        : findNodeIdByLabel(productOptions.value, productName) || ''
+
     // 濡傛灉鏈変骇鍝両D锛屽姞杞藉搴旂殑瑙勬牸鍨嬪彿鍒楄〃
     let modelOptions = [];
     let resolvedSpecificationId = product.specificationId || '';
-    
+
     if (resolvedProductId) {
       try {
-        const res = await modelList({ id: resolvedProductId });
+        const res = await modelList({id: resolvedProductId});
         modelOptions = res || [];
-        
+
         // 濡傛灉杩斿洖鐨勬暟鎹病鏈� specificationId锛屼絾鏈� specification 鍚嶇О锛屾牴鎹悕绉版煡鎵� ID
         if (!resolvedSpecificationId && product.specification) {
           const foundModel = modelOptions.find(item => item.model === product.specification);
@@ -912,7 +972,7 @@
         console.error('鍔犺浇瑙勬牸鍨嬪彿澶辫触:', error);
       }
     }
-    
+
     return {
       productId: resolvedProductId,
       product: productName,
@@ -931,7 +991,7 @@
   form.discountRate = row.discountRate || 0
   form.discountAmount = row.discountAmount || 0
   form.totalAmount = row.totalAmount || 0
-  
+
   // 鍙嶆樉瀹℃壒浜�
   if (row.approveUserIds) {
     const userIds = row.approveUserIds.split(',')
@@ -941,10 +1001,10 @@
     }))
     nextApproverId = userIds.length + 1
   } else {
-    approverNodes.value = [{ id: 1, userId: null }]
+    approverNodes.value = [{id: 1, userId: null}]
     nextApproverId = 2
   }
-  
+
   // 鍔犺浇鐢ㄦ埛鍒楄〃
   let userLists = await userListNoPage();
   userList.value = (userLists.data || []).map(item => ({
@@ -952,7 +1012,7 @@
     nickName: item.nickName || '',
     userName: item.userName || ''
   }));
-  
+
   dialogVisible.value = true
 }
 
@@ -965,9 +1025,9 @@
   }).then(() => {
     const index = quotationList.value.findIndex(item => item.id === row.id)
     if (index > -1) {
-      deleteQuotation(row.id).then(res=>{
+      deleteQuotation(row.id).then(res => {
         // console.log(res)
-        if(res.code===200){
+        if (res.code === 200) {
           ElMessage.success('鍒犻櫎鎴愬姛')
           handleSearch()
         }
@@ -1049,23 +1109,23 @@
         ElMessage.error('璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒')
         return
       }
-      
+
       // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
       form.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
-      
+
       // 璁$畻鎵�鏈変骇鍝佺殑鍗曚环鎬诲拰
       form.totalAmount = form.products.reduce((sum, product) => {
         const price = Number(product.unitPrice) || 0
         return sum + price
       }, 0)
-      
+
       if (isEdit.value) {
         // 缂栬緫
         const index = quotationList.value.findIndex(item => item.id === editId.value)
         if (index > -1) {
-          updateQuotation(form).then(res=>{
+          updateQuotation(form).then(res => {
             // console.log(res)
-            if(res.code===200){
+            if (res.code === 200) {
               ElMessage.success('缂栬緫鎴愬姛')
               dialogVisible.value = false
               handleSearch()
@@ -1074,17 +1134,21 @@
         }
       } else {
         // 鏂板
-        addQuotation(form).then(res=>{
-          if(res.code===200){
+        addQuotation(form).then(res => {
+          if (res.code === 200) {
             ElMessage.success('鏂板鎴愬姛')
             dialogVisible.value = false
             handleSearch()
           }
         })
       }
-      
+
     }
   })
+}
+
+const downloadImportTemplate = () => {
+  proxy.download("/sales/quotation/downloadTemplate", {}, "鎶ヤ环鍗曞鍏ユā鏉�.xlsx");
 }
 
 const handleCurrentChange = (val) => {
@@ -1093,16 +1157,16 @@
   // 鍒嗛〉鍙樺寲鏃堕噸鏂版煡璇㈠垪琛�
   handleSearch()
 }
-const handleSearch = ()=>{
+const handleSearch = () => {
   const params = {
     // 鍚庣鍒嗛〉鍙傛暟锛歝urrent / size
     current: pagination.currentPage,
     size: pagination.pageSize,
     ...searchForm
   }
-  getQuotationList(params).then(res=>{
+  getQuotationList(params).then(res => {
     // console.log(res)
-    if(res.code===200){
+    if (res.code === 200) {
       // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鎴栧叾浠栧璞℃斁鍏ュ搷搴斿紡瀵硅薄
       quotationList.value = (res.data.records || []).map(item => ({
         id: item.id,
@@ -1136,17 +1200,23 @@
       pagination.total = res.data.total
     }
   })
-	customerList().then((res) => {
-		// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
-		customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
+  customerList().then((res) => {
+    // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
+    customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
       id: item.id,
       customerName: item.customerName || '',
       taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
     }))
-	});
+  });
 }
 
-onMounted(()=>{
+const handleImportDetail = (row) => {
+  showDetail.value = true
+  currentQuotation.value = row
+}
+
+
+onMounted(() => {
   handleSearch()
 })
 </script>
@@ -1160,16 +1230,16 @@
   padding: 10px 0;
   max-height: calc(100vh - 200px);
   overflow-y: auto;
-  
+
   &::-webkit-scrollbar {
     width: 6px;
     height: 6px;
   }
-  
+
   &::-webkit-scrollbar-thumb {
     background: #c1c1c1;
     border-radius: 3px;
-    
+
     &:hover {
       background: #a8a8a8;
     }
@@ -1186,17 +1256,17 @@
   margin-bottom: 24px;
   border-radius: 8px;
   transition: all 0.3s ease;
-  
+
   &:hover {
     box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
   }
-  
+
   :deep(.el-card__header) {
     padding: 16px 20px;
     background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
     border-bottom: 1px solid #ebeef5;
   }
-  
+
   :deep(.el-card__body) {
     padding: 20px;
   }
@@ -1206,19 +1276,19 @@
   display: flex;
   align-items: center;
   gap: 8px;
-  
+
   .card-icon {
     font-size: 18px;
     color: #409eff;
   }
-  
+
   .card-title {
     font-weight: 600;
     font-size: 16px;
     color: #303133;
     flex: 1;
   }
-  
+
   .header-btn {
     margin-left: auto;
   }
@@ -1230,9 +1300,11 @@
 
 .product-table-form-item {
   margin-bottom: 0;
+
   :deep(.el-form-item__content) {
     margin-left: 0 !important;
   }
+
   :deep(.el-form-item__label) {
     width: auto;
     min-width: auto;
@@ -1257,7 +1329,7 @@
   border: 1px solid #e4e7ed;
   transition: all 0.3s ease;
   min-width: 180px;
-  
+
   &:hover {
     border-color: #409eff;
     background: #f0f7ff;
@@ -1271,7 +1343,7 @@
   gap: 8px;
   font-size: 14px;
   color: #606266;
-  
+
   .node-step {
     display: inline-flex;
     align-items: center;
@@ -1284,11 +1356,11 @@
     font-size: 12px;
     font-weight: 600;
   }
-  
+
   .node-text {
     font-weight: 500;
   }
-  
+
   .arrow-icon {
     color: #909399;
     font-size: 14px;
@@ -1307,20 +1379,20 @@
 .product-table {
   :deep(.el-table__header) {
     background-color: #f5f7fa;
-    
+
     th {
       background-color: #f5f7fa !important;
       color: #606266;
       font-weight: 600;
     }
   }
-  
+
   :deep(.el-table__row) {
     &:hover {
       background-color: #f5f7fa;
     }
   }
-  
+
   :deep(.el-table__cell) {
     padding: 12px 0;
   }
@@ -1335,7 +1407,7 @@
   .approver-nodes-container {
     gap: 16px;
   }
-  
+
   .approver-node-item {
     min-width: 160px;
   }

--
Gitblit v1.9.3