From 7726b6cdab80596d2e2f7dd3fe1ec3dfbdeee155 Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期五, 12 六月 2026 09:42:40 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_pro_河南鹤壁' into dev_pro_河南鹤壁

---
 src/views/procurementManagement/procurementLedger/index.vue | 1725 ++++++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 1,180 insertions(+), 545 deletions(-)

diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index d498c29..9bbe87e 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -2,190 +2,132 @@
   <div class="app-container">
     <div class="search_form">
       <div>
-        <span class="search_title">閲囪喘鍚堝悓鍙凤細</span>
-        <el-input
-          v-model="searchForm.purchaseContractNumber"
-          style="width: 240px"
-          placeholder="璇疯緭鍏�"
-          @change="handleQuery"
-          clearable
-          :prefix-icon="Search"
-        />
-        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
-          >鎼滅储</el-button
-        >
-      </div>
-      <div>
-        <el-button type="primary" @click="openForm('add')">鏂板鍙拌处</el-button>
-        <el-button @click="handleOut">瀵煎嚭</el-button>
-        <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+        <el-form :model="searchForm" :inline="true">
+          <el-form-item label="渚涘簲鍟嗗悕绉帮細">
+            <el-input v-model="searchForm.supplierName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+              @change="handleQuery" />
+          </el-form-item>
+          <el-form-item label="閲囪喘鍚堝悓鍙凤細">
+            <el-input v-model="searchForm.purchaseContractNumber" style="width: 240px" placeholder="璇疯緭鍏�"
+              @change="handleQuery" clearable :prefix-icon="Search" />
+          </el-form-item>
+          <el-form-item v-if="showSalesContractBinding" label="閿�鍞悎鍚屽彿锛�">
+            <el-input v-model="searchForm.salesContractNo" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+              @change="handleQuery" />
+          </el-form-item>
+          <el-form-item label="椤圭洰鍚嶇О锛�">
+            <el-input v-model="searchForm.projectName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+              @change="handleQuery" />
+          </el-form-item>
+          <el-form-item label="褰曞叆鏃ユ湡锛�">
+            <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
+              type="daterange" placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+          </el-form-item>
+          <el-form-item label="鍏ュ簱鐘舵�侊細">
+            <el-select v-model="searchForm.stockInStatus" placeholder="璇烽�夋嫨" clearable style="width: 240px"
+              @change="handleQuery">
+              <el-option label="寰呭叆搴�" value="寰呭叆搴�" />
+              <el-option label="鍏ュ簱涓�" value="鍏ュ簱涓�" />
+              <el-option label="瀹屽叏鍏ュ簱" value="瀹屽叏鍏ュ簱" />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="handleQuery"> 鎼滅储
+            </el-button>
+          </el-form-item>
+        </el-form>
       </div>
     </div>
     <div class="table_list">
-      <el-table
-        :data="tableData"
-        border
-        v-loading="tableLoading"
-        @selection-change="handleSelectionChange"
-        :expand-row-keys="expandedRowKeys"
-        :row-key="(row) => row.id"
-        show-summary
-        :summary-method="summarizeMainTable"
-        @expand-change="expandChange"
-        height="calc(100vh - 18.5em)"
-      >
+      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
+        <el-button type="primary" @click="openForm('add')">鏂板鍙拌处</el-button>
+        <el-button type="primary" plain @click="handleImport">瀵煎叆</el-button>
+        <el-button @click="handleOut">瀵煎嚭</el-button>
+        <el-button type="danger" plain @click="handleDelete">鍒犻櫎
+        </el-button>
+      </div>
+      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
+        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary :summary-method="summarizeMainTable"
+        @expand-change="expandChange" height="calc(100vh - 21.5em)">
         <el-table-column align="center" type="selection" width="55" />
         <el-table-column type="expand">
           <template #default="props">
-            <el-table
-              :data="props.row.children"
-              border
-              show-summary
-              :summary-method="summarizeChildrenTable"
-            >
-              <el-table-column
-                align="center"
-                label="搴忓彿"
-                type="index"
-                width="60"
-              />
+            <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
+              <el-table-column align="center" label="搴忓彿" type="index" width="60" />
               <el-table-column label="浜у搧澶х被" prop="productCategory" />
               <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
               <el-table-column label="鍗曚綅" prop="unit" />
+              <el-table-column label="鍏ュ簱瀹℃牳鐘舵��" prop="stockInApprovalStatus" width="120">
+                <template #default="scope">
+                  <el-tag :type="getStockInApprovalStatusType(scope.row.stockInApprovalStatus)" size="small">
+                    {{ scope.row.stockInApprovalStatus || '--' }}
+                  </el-tag>
+                </template>
+              </el-table-column>
               <el-table-column label="鏁伴噺" prop="quantity" />
+              <el-table-column label="鍙敤鏁伴噺" prop="availableQuality" />
+              <el-table-column label="閫�璐ф暟閲�" prop="returnQuality" />
               <el-table-column label="绋庣巼(%)" prop="taxRate" />
-              <el-table-column
-                label="鍚◣鍗曚环(鍏�)"
-                prop="taxInclusiveUnitPrice"
-                :formatter="formattedNumber"
-              />
-              <el-table-column
-                label="鍚◣鎬讳环(鍏�)"
-                prop="taxInclusiveTotalPrice"
-                :formatter="formattedNumber"
-              />
-              <el-table-column
-                label="涓嶅惈绋庢�讳环(鍏�)"
-                prop="taxExclusiveTotalPrice"
-                :formatter="formattedNumber"
-              />
+              <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
+              <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
+              <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
             </el-table>
           </template>
         </el-table-column>
         <el-table-column align="center" label="搴忓彿" type="index" width="60" />
-        <el-table-column
-          label="閲囪喘鍚堝悓鍙�"
-          prop="purchaseContractNumber"
-          width="200"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="閿�鍞悎鍚屽彿"
-          prop="salesContractNo"
-          width="200"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="渚涘簲鍟嗗悕绉�"
-          width="240"
-          prop="supplierName"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="椤圭洰鍚嶇О"
-          prop="projectName"
-          width="420"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="浠樻鏂瑰紡"
-          width="100"
-          prop="paymentMethod"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="鍚堝悓閲戦(鍏�)"
-          prop="contractAmount"
-           width="200"
-          show-overflow-tooltip
-          :formatter="formattedNumber"
-        />
-        <el-table-column
-          label="褰曞叆浜�"
-          prop="recorderName"
-           width="100"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="褰曞叆鏃ユ湡"
-          prop="entryDate"
-           width="100"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          fixed="right"
-          label="鎿嶄綔"
-          min-width="60"
-          align="center"
-        >
+        <el-table-column label="閲囪喘鍚堝悓鍙�" prop="purchaseContractNumber" width="160" show-overflow-tooltip />
+        <el-table-column v-if="showSalesContractBinding" label="閿�鍞悎鍚屽彿" prop="salesContractNo" width="160" show-overflow-tooltip />
+        <el-table-column label="渚涘簲鍟嗗悕绉�" prop="supplierName" width="160" show-overflow-tooltip />
+        <el-table-column label="椤圭洰鍚嶇О" prop="projectName" width="320" show-overflow-tooltip />
+        <el-table-column label="瀹℃壒鐘舵��" prop="approvalStatus" width="100" show-overflow-tooltip>
           <template #default="scope">
-            <el-button
-              link
-              type="primary"
-              size="small"
-              @click="openForm('edit', scope.row)"
-							:disabled="scope.row.receiptPaymentAmount>0 || scope.row.recorderName !== userStore.nickName"
-              >缂栬緫</el-button
-            >
+            <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" size="small">
+              {{ approvalStatusText[scope.row.approvalStatus] || '鏈煡鐘舵��' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鍏ュ簱鐘舵��" prop="stockInStatus" width="100" show-overflow-tooltip>
+          <template #default="scope">
+            <el-tag :type="getStockInStatusType(scope.row.stockInStatus)" size="small">
+              {{ scope.row.stockInStatus || '--' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="100" show-overflow-tooltip />
+        <el-table-column label="浠樻鏂瑰紡" width="100" prop="paymentMethod" show-overflow-tooltip />
+        <el-table-column label="鍚堝悓閲戦(鍏�)" prop="contractAmount" width="200" show-overflow-tooltip
+          :formatter="formattedNumber" />
+        <el-table-column label="褰曞叆浜�" prop="recorderName" width="120" show-overflow-tooltip />
+        <el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="100" show-overflow-tooltip />
+        <el-table-column label="澶囨敞" prop="remarks" width="200" show-overflow-tooltip />
+        <el-table-column fixed="right" label="鎿嶄綔" width="180" align="center">
+          <template #default="scope">
+            <el-button link type="primary" @click="openForm('detail', scope.row)">璇︽儏
+            </el-button>
+            <el-button link type="primary" @click="openForm('edit', scope.row)"
+              :disabled="scope.row.stockInStatus === '瀹屽叏鍏ュ簱'">缂栬緫
+            </el-button>
+            <el-button link type="primary" @click="openFileDialog(scope.row)">闄勪欢</el-button>
           </template>
         </el-table-column>
       </el-table>
-      <pagination
-        v-show="total > 0"
-        :total="total"
-        layout="total, sizes, prev, pager, next, jumper"
-        :page="page.current"
-        :limit="page.size"
-        @pagination="paginationChange"
-      />
+      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
+        :page="page.current" :limit="page.size" @pagination="paginationChange" />
     </div>
-    <el-dialog
-      v-model="dialogFormVisible"
-      :title="operationType === 'add' ? '鏂板閲囪喘鍙拌处椤甸潰' : '缂栬緫閲囪喘鍙拌处椤甸潰'"
-      width="70%"
-      @close="closeDia"
-    >
-      <el-form
-        :model="form"
-        label-width="140px"
-        label-position="top"
-        :rules="rules"
-        ref="formRef"
-      >
+    <FormDialog v-model="dialogFormVisible"
+      :title="operationType === 'add' ? '鏂板閲囪喘鍙拌处椤甸潰' : operationType === 'edit' ? '缂栬緫閲囪喘鍙拌处椤甸潰' : '閲囪喘鍙拌处璇︽儏'" :width="'70%'"
+      :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia">
+      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="閲囪喘鍚堝悓鍙凤細" prop="purchaseContractNumber">
-              <el-input
-                v-model="form.purchaseContractNumber"
-                placeholder="璇疯緭鍏�"
-                clearable
-              />
+              <el-input v-model="form.purchaseContractNumber" placeholder="璇疯緭鍏�" clearable />
             </el-form-item>
           </el-col>
-          <el-col :span="12">
+          <el-col v-if="showSalesContractBinding" :span="12">
             <el-form-item label="閿�鍞悎鍚屽彿锛�" prop="salesLedgerId">
-              <el-select
-                v-model="form.salesLedgerId"
-                placeholder="璇烽�夋嫨"
-                clearable
-                @change="salesLedgerChange"
-              >
-                <el-option
-                  v-for="item in salesContractList"
-                  :key="item.id"
-                  :label="item.salesContractNo"
-                  :value="item.id"
-                />
+              <el-select v-model="form.salesLedgerId" placeholder="璇烽�夋嫨" filterable clearable @change="salesLedgerChange">
+                <el-option v-for="item in salesContractList" :key="item.id" :label="item.salesContractNo" :value="item.id" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -193,229 +135,160 @@
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="渚涘簲鍟嗗悕绉帮細" prop="supplierId">
-              <el-select
-                v-model="form.supplierId"
-                placeholder="璇烽�夋嫨"
-                clearable
-              >
-                <el-option
-                  v-for="item in supplierList"
-                  :key="item.id"
-                  :label="item.supplierName"
-                  :value="item.id"
-                />
+              <el-select v-model="form.supplierId" placeholder="璇烽�夋嫨" filterable clearable>
+                <el-option v-for="item in supplierList" :key="item.id" :label="item.supplierName" :value="item.id">{{ item.supplierName + '---' + item.supplierType }}</el-option>
               </el-select>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
-              <el-input
-                v-model="form.projectName"
-                placeholder="璇疯緭鍏�"
-                clearable
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="褰曞叆浜猴細" prop="recorderId">
-              <el-select
-                v-model="form.recorderId"
-                placeholder="璇烽�夋嫨"
-                clearable
-                disabled
-              >
-                <el-option
-                  v-for="item in userList"
-                  :key="item.userId"
-                  :label="item.nickName"
-                  :value="item.userId"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="褰曞叆鏃ユ湡锛�" prop="entryDate">
-              <el-date-picker
-                disabled
-                style="width: 100%"
-                v-model="form.entryDate"
-                value-format="YYYY-MM-DD"
-                format="YYYY-MM-DD"
-                type="date"
-                placeholder="璇烽�夋嫨"
-                clearable
-              />
+            <el-form-item label="椤圭洰鍚嶇О" prop="projectName">
+              <el-input v-model="form.projectName" placeholder="璇疯緭鍏�" clearable />
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="浠樻鏂瑰紡">
-              <el-input
-                v-model="form.paymentMethod"
-                placeholder="璇疯緭鍏�"
-                clearable
-              />
+              <el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏�" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绛捐鏃ユ湡锛�" prop="executionDate">
+              <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="璇烽�夋嫨" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="褰曞叆浜猴細" prop="recorderId">
+              <el-select v-model="form.recorderId" placeholder="璇烽�夋嫨" clearable filterable>
+                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="褰曞叆鏃ユ湡锛�" prop="entryDate">
+              <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="璇烽�夋嫨" clearable />
             </el-form-item>
           </el-col>
         </el-row>
         <el-row>
           <el-form-item label="浜у搧淇℃伅锛�" prop="entryDate">
-            <el-button type="primary" @click="openProductForm('add')"
-              >娣诲姞</el-button
-            >
-            <el-button plain type="danger" @click="deleteProduct"
-              >鍒犻櫎</el-button
-            >
+            <el-button type="primary" @click="openProductForm('add')">娣诲姞</el-button>
+            <el-button plain type="danger" @click="deleteProduct">鍒犻櫎</el-button>
           </el-form-item>
+          <div class="select-button-group" style="width: 500px; margin: 20px 0;" v-if="operationType === 'add'">
+            <el-select filterable allow-create :reserve-keyword="true" :default-first-option="false" clearable
+              v-model="templateName" :input-value="filterInputValue" @filter-change="onTemplateFilterChange"
+              @change="onTemplateChange" @focus="getTemplateList" style="width: 500px;" placeholder="璇烽�夋嫨妯$増鎴栬�呰緭鍏ユ柊鐨勬ā鐗堝悕绉板悗閫夋嫨"
+              class="no-arrow-select">
+              <el-option v-for="item in templateList" :key="item.id || item.value" :label="item.templateName"
+                :value="item.templateName">
+                <div style="display: flex; justify-content: space-between; align-items: center;">
+                  <span>{{ item.templateName }}</span>
+                  <el-icon v-if="item.id" class="delete-icon" @click.stop="handleDeleteTemplate(item)"
+                    style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;">
+                    <Delete />
+                  </el-icon>
+                </div>
+              </el-option>
+            </el-select>
+            <!-- 鎸夐挳锛氫笌 Select 楂樺害鍖归厤锛屽幓鎺夊乏渚ц竟妗嗭紝鏃犵紳琛旀帴 -->
+            <el-button size="small" style="height: 32px;margin-left: 8px;" @click="handleButtonClick"
+              :disabled="!templateName || templateName.trim() === '' || (!currentTemplateId && isTemplateNameDuplicate)">
+              淇濆瓨
+            </el-button>
+          </div>
         </el-row>
-        <el-table
-          :data="productData"
-          border
-          @selection-change="productSelected"
-          show-summary
-          :summary-method="summarizeProTable"
-        >
+        <el-table :data="productData" border @selection-change="productSelected" show-summary
+          :summary-method="summarizeProTable">
           <el-table-column align="center" type="selection" width="55" />
-          <el-table-column
-            align="center"
-            label="搴忓彿"
-            type="index"
-            width="60"
-          />
+          <el-table-column align="center" label="搴忓彿" type="index" width="60" />
           <el-table-column label="浜у搧澶х被" prop="productCategory" />
           <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
           <el-table-column label="鍗曚綅" prop="unit" width="70" />
           <el-table-column label="鏁伴噺" prop="quantity" width="70" />
+          <el-table-column label="搴撳瓨棰勮鏁伴噺" prop="warnNum" width="120" show-overflow-tooltip />
           <el-table-column label="绋庣巼(%)" prop="taxRate" width="80" />
-          <el-table-column
-            label="鍚◣鍗曚环(鍏�)"
-            prop="taxInclusiveUnitPrice"
-            :formatter="formattedNumber"
-            width="150"
-          />
-          <el-table-column
-            label="鍚◣鎬讳环(鍏�)"
-            prop="taxInclusiveTotalPrice"
-            :formatter="formattedNumber"
-            width="150"
-          />
-          <el-table-column
-            label="涓嶅惈绋庢�讳环(鍏�)"
-            prop="taxExclusiveTotalPrice"
-            :formatter="formattedNumber"
-            width="150"
-          />
-          <el-table-column
-            fixed="right"
-            label="鎿嶄綔"
-            min-width="60"
-            align="center"
-          >
+          <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="150" />
+          <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="150" />
+          <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" width="150" />
+          <el-table-column label="鏄惁璐ㄦ" prop="isChecked" width="150">
             <template #default="scope">
-              <el-button
-                link
-                type="primary"
-                size="small"
+              <el-tag :type="scope.row.isChecked ? 'success' : 'info'">
+                {{ scope.row.isChecked ? '鏄�' : '鍚�' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="鍏ュ簱瀹℃牳鐘舵��" prop="stockInApprovalStatus" width="120">
+            <template #default="scope">
+              <el-tag :type="getStockInApprovalStatusType(scope.row.stockInApprovalStatus)" size="small">
+                {{ scope.row.stockInApprovalStatus || '--' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center">
+            <template #default="scope">
+              <el-button link type="primary"
                 @click="openProductForm('edit', scope.row, scope.$index)"
-                >缂栬緫</el-button
-              >
+                :disabled="scope.row.stockInApprovalStatus === '瀹屽叏鍏ュ簱'">缂栬緫
+              </el-button>
             </template>
           </el-table-column>
         </el-table>
         <el-row :gutter="30">
           <el-col :span="24">
-            <el-form-item label="澶囨敞路锛�" prop="remark">
-              <el-input
-                v-model="form.remark"
-                placeholder="璇疯緭鍏�"
-                clearable
-                type="textarea"
-                :rows="2"
-              />
+            <el-form-item label="澶囨敞路锛�" prop="remarks">
+              <el-input v-model="form.remarks" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" />
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="30">
           <el-col :span="24">
-            <el-form-item label="闄勪欢鏉愭枡锛�" prop="remark">
-              <el-upload
-                v-model:file-list="fileList"
-                :action="upload.url"
-                multiple
-                ref="fileUpload"
-                auto-upload
-                :headers="upload.headers"
-                :before-upload="handleBeforeUpload"
-                :on-error="handleUploadError"
-                :on-success="handleUploadSuccess"
-                :on-remove="handleRemove"
-              >
-                <el-button type="primary">涓婁紶</el-button>
-                <template #tip>
-                  <div class="el-upload__tip">
-                    鏂囦欢鏍煎紡鏀寔
-                    doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
-                  </div>
-                </template>
-              </el-upload>
+            <el-form-item label="闄勪欢鏉愭枡锛�" prop="purchaseLedgerFiles">
+              <FileUpload v-model:file-list="fileList" />
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary" @click="submitForm">纭</el-button>
-          <el-button @click="closeDia">鍙栨秷</el-button>
+    </FormDialog>
+    <!-- 瀵煎叆寮圭獥 -->
+    <FormDialog v-model="importUpload.open" :title="importUpload.title" :width="'600px'"
+      @close="importUpload.open = false" @confirm="submitImportFile" @cancel="importUpload.open = false">
+      <el-upload ref="importUploadRef" :limit="1" accept=".xlsx,.xls" :action="importUpload.url"
+        :headers="importUpload.headers" :before-upload="importUpload.beforeUpload" :on-success="importUpload.onSuccess"
+        :on-error="importUpload.onError" :on-progress="importUpload.onProgress" :on-change="importUpload.onChange"
+        :auto-upload="false" drag>
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">
+          灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em>
         </div>
-      </template>
-    </el-dialog>
-    <el-dialog
-      v-model="productFormVisible"
-      :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
-      width="40%"
-      @close="closeProductDia"
-    >
-      <el-form
-        :model="productForm"
-        label-width="140px"
-        label-position="top"
-        :rules="productRules"
-        ref="productFormRef"
-      >
+        <template #tip>
+          <div class="el-upload__tip">
+            浠呮敮鎸� xls/xlsx锛屽ぇ灏忎笉瓒呰繃 10MB銆�
+            <el-button link type="primary" @click="downloadTemplate">涓嬭浇瀵煎叆妯℃澘</el-button>
+          </div>
+        </template>
+      </el-upload>
+    </FormDialog>
+    <FormDialog v-model="productFormVisible" :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'" :width="'40%'"
+      :operation-type="productOperationType" @close="closeProductDia" @confirm="submitProduct"
+      @cancel="closeProductDia">
+      <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
         <el-row :gutter="30">
           <el-col :span="24">
             <el-form-item label="浜у搧澶х被锛�" prop="productId">
-              <el-tree-select
-                v-model="productForm.productId"
-                placeholder="璇烽�夋嫨"
-                clearable
-                check-strictly
-                @change="getModels"
-                :data="productOptions"
-                :render-after-expand="false"
-                style="width: 100%"
-              />
+              <el-tree-select v-model="productForm.productId" placeholder="璇烽�夋嫨" clearable filterable check-strictly
+                @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="30">
           <el-col :span="24">
             <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="productModelId">
-              <el-select
-                v-model="productForm.productModelId"
-                placeholder="璇烽�夋嫨"
-                clearable
-                @change="getProductModel"
-              >
-                <el-option
-                  v-for="item in modelOptions"
-                  :key="item.id"
-                  :label="item.model"
-                  :value="item.id"
-                />
+              <el-select v-model="productForm.productModelId" placeholder="璇烽�夋嫨" filterable clearable
+                @change="getProductModel">
+                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -423,114 +296,97 @@
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="鍗曚綅锛�" prop="unit">
-              <el-input
-                v-model="productForm.unit"
-                placeholder="璇疯緭鍏�"
-                clearable
-              />
+              <el-input v-model="productForm.unit" placeholder="璇疯緭鍏�" clearable />
             </el-form-item>
           </el-col>
-					<el-col :span="12">
-						<el-form-item label="绋庣巼(%)锛�" prop="taxRate">
-							<el-select
-								v-model="productForm.taxRate"
-								placeholder="璇烽�夋嫨"
-								clearable
-								@change="mathNum"
-							>
-								<el-option label="1" value="1" />
-								<el-option label="6" value="6" />
-								<el-option label="13" value="13" />
-							</el-select>
-						</el-form-item>
-					</el-col>
+          <el-col :span="12">
+            <el-form-item label="绋庣巼(%)锛�" prop="taxRate">
+              <el-select v-model="productForm.taxRate" placeholder="璇烽�夋嫨" clearable @change="mathNum">
+                <el-option v-for="dict in tax_rate" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="鍚◣鍗曚环(鍏�)锛�" prop="taxInclusiveUnitPrice">
-              <el-input-number
-                v-model="productForm.taxInclusiveUnitPrice"
-                :precision="2"
-                :step="0.1"
-                clearable
-                style="width: 100%"
-                @change="mathNum"
-              />
+              <el-input-number v-model="productForm.taxInclusiveUnitPrice" :precision="2" :step="0.1" :min="0" clearable
+                style="width: 100%" @change="mathNum" />
             </el-form-item>
           </el-col>
-					<el-col :span="12">
-						<el-form-item label="鏁伴噺锛�" prop="quantity">
-							<el-input-number
-								:step="0.1"
-								clearable
-								:precision="2"
-								style="width: 100%"
-								v-model="productForm.quantity"
-								placeholder="璇疯緭鍏�"
-								@change="mathNum"
-							/>
-						</el-form-item>
-					</el-col>
+          <el-col :span="12">
+            <el-form-item label="鏁伴噺锛�" prop="quantity">
+              <el-input-number :step="0.1" clearable :precision="2" :min="0" style="width: 100%"
+                v-model="productForm.quantity" placeholder="璇疯緭鍏�" @change="mathNum" />
+            </el-form-item>
+          </el-col>
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="鍚◣鎬讳环(鍏�)锛�" prop="taxInclusiveTotalPrice">
-              <el-input-number
-                v-model="productForm.taxInclusiveTotalPrice"
-                :precision="2"
-                :step="0.1"
-                clearable
-                style="width: 100%"
-                @change="reverseMathNum('taxInclusiveTotalPrice')"
-              />
+              <el-input-number v-model="productForm.taxInclusiveTotalPrice" :precision="2" :step="0.1" :min="0"
+                clearable style="width: 100%" @change="reverseMathNum('taxInclusiveTotalPrice')" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item
-              label="涓嶅惈绋庢�讳环(鍏�)锛�"
-              prop="taxExclusiveTotalPrice"
-            >
-              <el-input 
-                v-model="productForm.taxExclusiveTotalPrice"
-                @change="reverseMathNum('taxExclusiveTotalPrice')"
-              />
+            <el-form-item label="涓嶅惈绋庢�讳环(鍏�)锛�" prop="taxExclusiveTotalPrice">
+              <el-input-number v-model="productForm.taxExclusiveTotalPrice" :precision="2" :step="0.1" :min="0"
+                clearable style="width: 100%" @change="reverseMathNum('taxExclusiveTotalPrice')" />
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="鍙戠エ绫诲瀷锛�" prop="invoiceType">
-              <el-select
-                v-model="productForm.invoiceType"
-                placeholder="璇烽�夋嫨"
-                clearable
-              >
+              <el-select v-model="productForm.invoiceType" placeholder="璇烽�夋嫨" clearable>
                 <el-option label="澧炴櫘绁�" value="澧炴櫘绁�" />
                 <el-option label="澧炰笓绁�" value="澧炰笓绁�" />
               </el-select>
             </el-form-item>
           </el-col>
+          <el-col :span="12">
+            <el-form-item label="搴撳瓨棰勮鏁伴噺锛�" prop="warnNum">
+              <el-input-number v-model="productForm.warnNum" :precision="2" :step="0.1" :min="0" clearable
+                style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鏄惁璐ㄦ锛�" prop="isChecked">
+              <el-radio-group v-model="productForm.isChecked">
+                <el-radio label="鏄�" :value="true" />
+                <el-radio label="鍚�" :value="false" />
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
         </el-row>
       </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary" @click="submitProduct">纭</el-button>
-          <el-button @click="closeProductDia">鍙栨秷</el-button>
-        </div>
-      </template>
-    </el-dialog>
+    </FormDialog>
+    <FileList v-if="fileListDialogVisible" v-model:visible="fileListDialogVisible" record-type="purchase_ledger"
+      :record-id="recordId" />
+    <Detail ref="detailRef" />
   </div>
 </template>
 
 <script setup>
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import FileListDialog from "@/components/Dialog/FileListDialog.vue";
+import Detail from "./detail.vue";
 import { getToken } from "@/utils/auth";
 import pagination from "@/components/PIMTable/Pagination.vue";
-import { ref, onMounted } from "vue";
-import { Search } from "@element-plus/icons-vue";
-import { ElMessageBox } from "element-plus";
+import {
+  ref,
+  onMounted,
+  reactive,
+  toRefs,
+  getCurrentInstance,
+  nextTick,
+} from "vue";
+import { Search, Delete } from "@element-plus/icons-vue";
+import { ElMessageBox, ElMessage } from "element-plus";
 import { userListNoPage } from "@/api/system/user.js";
 import {
-  getSalesLedgerWithProducts,
   addOrUpdateSalesLedgerProduct,
   delProduct,
   delLedgerFile,
@@ -538,15 +394,27 @@
 } from "@/api/salesManagement/salesLedger.js";
 import {
   addOrEditPurchase,
+  addPurchaseTemplate,
+  updatePurchaseTemplate,
+  createPurchaseNo,
   delPurchase,
   getSalesNo,
   purchaseListPage,
   productList,
   getPurchaseById,
   getOptions,
-  createPurchaseNo,
+  getPurchaseTemplateList,
+  delPurchaseTemplate,
 } from "@/api/procurementManagement/procurementLedger.js";
+import useFormData from "@/hooks/useFormData.js";
+const FileList = defineAsyncComponent(() =>
+  import("@/components/Dialog/FileList.vue")
+);
+
+const detailRef = ref(null);
+
 const { proxy } = getCurrentInstance();
+const { tax_rate } = proxy.useDict("tax_rate");
 const tableData = ref([]);
 const productData = ref([]);
 const selectedRows = ref([]);
@@ -557,6 +425,8 @@
 const salesContractList = ref([]);
 const supplierList = ref([]);
 const tableLoading = ref(false);
+const recordId = ref();
+const fileListDialogVisible = ref(false);
 const page = reactive({
   current: 1,
   size: 100,
@@ -565,15 +435,174 @@
 const fileList = ref([]);
 import useUserStore from "@/store/modules/user";
 import { modelList, productTreeList } from "@/api/basicData/product.js";
+import dayjs from "dayjs";
+import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 
 const userStore = useUserStore();
+
+// 鏄惁鏄剧ず閿�鍞悎鍚屽彿缁戝畾
+const showSalesContractBinding = false;
+
+// 璁㈠崟瀹℃壒鐘舵�佹樉绀烘枃鏈�
+const approvalStatusText = {
+  1: "寰呭鏍�",
+  2: "瀹℃壒涓�",
+  3: "瀹℃壒閫氳繃",
+  4: "瀹℃壒澶辫触",
+};
+
+// 鑾峰彇瀹℃壒鐘舵�佹爣绛剧被鍨�
+const getApprovalStatusType = status => {
+  const typeMap = {
+    1: "info", // 寰呭鏍� - 鐏拌壊
+    2: "warning", // 瀹℃壒涓� - 姗欒壊
+    3: "success", // 瀹℃壒閫氳繃 - 缁胯壊
+    4: "danger", // 瀹℃壒澶辫触 - 绾㈣壊
+  };
+  return typeMap[status] || "";
+};
+
+// 鑾峰彇鍏ュ簱鐘舵�佹爣绛剧被鍨�
+const getStockInStatusType = status => {
+  const typeMap = {
+    "寰呭叆搴�": "info", // 寰呭叆搴� - 鐏拌壊
+    "鍏ュ簱涓�": "warning", // 鍏ュ簱涓� - 姗欒壊
+    "瀹屽叏鍏ュ簱": "success", // 瀹屽叏鍏ュ簱 - 缁胯壊
+  };
+  return typeMap[status] || "";
+};
+
+// 鑾峰彇鍏ュ簱瀹℃牳鐘舵�佹爣绛剧被鍨�
+const getStockInApprovalStatusType = status => {
+  const typeMap = {
+    "寰呭叆搴�": "info", // 寰呭叆搴� - 鐏拌壊
+    "鍏ュ簱涓�": "warning", // 鍏ュ簱涓� - 姗欒壊
+    "瀹屽叏鍏ュ簱": "success", // 瀹屽叏鍏ュ簱 - 缁胯壊
+  };
+  return typeMap[status] || "";
+};
+
+const templateName = ref("");
+const filterInputValue = ref("");
+const templateList = ref([]);
+const isTemplateNameDuplicate = ref(false); // 鏍囪妯℃澘鍚嶇О鏄惁閲嶅
+// 褰撳墠閫変腑鐨勬ā鏉縄D锛堢敤浜庡尯鍒嗘柊澧炴ā鏉胯繕鏄洿鏂版ā鏉匡級
+const currentTemplateId = ref(null);
+
+// 妫�鏌ユā鏉垮悕绉版槸鍚﹂噸澶�
+const checkTemplateNameDuplicate = name => {
+  if (!name || name.trim() === "") {
+    isTemplateNameDuplicate.value = false;
+    return false;
+  }
+  const isDuplicate = templateList.value.some(
+    item => item.templateName === name.trim()
+  );
+  isTemplateNameDuplicate.value = isDuplicate;
+  return isDuplicate;
+};
+
+// 鍒ゆ柇鏂囦欢鏄惁涓哄浘鐗�
+const isImage = (filename) => {
+  if (!filename) return false;
+  const ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
+  return ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'].includes(ext);
+};
+
+// 闃叉姈瀹氭椂鍣�
+let duplicateCheckTimer = null;
+const onTemplateFilterChange = val => {
+  filterInputValue.value = val ?? "";
+  // 娓呴櫎涔嬪墠鐨勫畾鏃跺櫒
+  if (duplicateCheckTimer) {
+    clearTimeout(duplicateCheckTimer);
+  }
+  // 瀹炴椂妫�鏌ユā鏉垮悕绉版槸鍚﹂噸澶嶏紙闃叉姈澶勭悊锛岄伩鍏嶉绻佹彁绀猴級
+  if (val && val.trim()) {
+    duplicateCheckTimer = setTimeout(() => {
+      const isDuplicate = checkTemplateNameDuplicate(val);
+      if (isDuplicate) {
+        ElMessage({
+          message: "妯℃澘鍚嶇О宸插瓨鍦紝璇锋洿鎹㈡ā鏉垮悕绉�",
+          type: "warning",
+          duration: 2000,
+        });
+      }
+    }, 300); // 300ms 闃叉姈
+  } else {
+    isTemplateNameDuplicate.value = false;
+  }
+};
+
+// allow-create 鏃讹紝杈撳叆涓嶅瓨鍦ㄧ殑鍐呭浼氫綔涓� string 鍊艰繑鍥烇紱杩欓噷鍚屾鍥炶緭鍏ユ浠ョ‘淇濇枃瀛椾笉涓�
+const onTemplateChange = async val => {
+  if (typeof val === "string") {
+    filterInputValue.value = val;
+    // 閫夋嫨鎴栬緭鍏ユ椂妫�鏌ラ噸澶�
+    checkTemplateNameDuplicate(val);
+  }
+
+  // 杩囨护鏁版嵁锛屾煡鎵惧尮閰嶇殑妯℃澘
+  const matchedTemplate = templateList.value.find(
+    item => item.templateName === val
+  );
+
+  if (matchedTemplate?.id) {
+    // 璁板綍褰撳墠閫変腑鐨勬ā鏉縄D锛屽悗缁繚瀛樻椂杩涜鏇存柊鎿嶄綔
+    currentTemplateId.value = matchedTemplate.id;
+    // 閫変腑宸叉湁妯℃澘鏃讹紝涓嶅簲瑙嗕负鈥滄ā鏉垮悕绉伴噸澶嶅鑷翠笉鍙繚瀛樷��
+    isTemplateNameDuplicate.value = false;
+    // 濡傛灉鎵惧埌妯℃澘锛屽彧璧嬪�间緵搴斿晢銆侀」鐩悕绉般�佷粯娆炬柟寮忓拰浜у搧淇℃伅
+    if (matchedTemplate.supplierId) {
+      form.value.supplierId = matchedTemplate.supplierId;
+    }
+    if (matchedTemplate.supplierName) {
+      form.value.supplierName = matchedTemplate.supplierName;
+    }
+    if (matchedTemplate.projectName) {
+      form.value.projectName = matchedTemplate.projectName;
+    }
+    if (matchedTemplate.paymentMethod) {
+      form.value.paymentMethod = matchedTemplate.paymentMethod;
+    }
+    // 妯℃澘鏁版嵁涓殑浜у搧瀛楁鏄� productList锛岄渶瑕佽浆鎹负 productData
+    productData.value =
+      matchedTemplate.productList || matchedTemplate.productData || [];
+  } else {
+    // 鏈尮閰嶅埌宸叉湁妯℃澘锛岃涓烘柊妯℃澘
+    currentTemplateId.value = null;
+    // 濡傛灉娌℃湁鎵惧埌妯℃澘锛岄噸缃〃鍗曪紙淇濇寔褰撳墠琛ㄥ崟鐘舵�侊級
+    const currentFormData = { ...form.value };
+    const currentProductData = [...productData.value];
+
+    // 濡傛灉瀵硅瘽妗嗘湭鎵撳紑锛屽厛鎵撳紑
+    if (!dialogFormVisible.value) {
+      operationType.value = "add";
+      dialogFormVisible.value = true;
+    }
+
+    // 绛夊緟涓嬩竴涓� tick 鍚庢仮澶嶆暟鎹�
+    await nextTick();
+    form.value = {
+      ...form.value,
+      ...currentFormData,
+    };
+    productData.value = currentProductData;
+  }
+};
 
 // 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
 const operationType = ref("");
 const dialogFormVisible = ref(false);
 const data = reactive({
   searchForm: {
-    purchaseContractNumber: "",
+    supplierName: "", // 渚涘簲鍟嗗悕绉�
+    purchaseContractNumber: "", // 閲囪喘鍚堝悓缂栧彿
+    salesContractNo: "", // 閿�鍞悎鍚岀紪鍙�
+    projectName: "", // 椤圭洰鍚嶇О
+    entryDate: null, // 褰曞叆鏃ユ湡
+    entryDateStart: undefined,
+    entryDateEnd: undefined,
   },
   form: {
     purchaseContractNumber: "",
@@ -585,16 +614,33 @@
     supplierName: "",
     supplierId: "",
     paymentMethod: "",
+    executionDate: "",
+    isChecked: false,
   },
   rules: {
     purchaseContractNumber: [
       { required: true, message: "璇疯緭鍏�", trigger: "blur" },
     ],
-    projectName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    projectName: [
+      { required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" },
+    ],
     supplierId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
   },
 });
-const { searchForm, form, rules } = toRefs(data);
+const { form, rules } = toRefs(data);
+const { form: searchForm } = useFormData({
+  ...data.searchForm,
+  // 璁剧疆褰曞叆鏃ユ湡鑼冨洿涓哄綋澶�
+  entryDate: [
+    dayjs().startOf("day").format("YYYY-MM-DD"),
+    dayjs().endOf("day").format("YYYY-MM-DD"),
+  ],
+  entryDateStart: dayjs().startOf("day").format("YYYY-MM-DD"),
+  entryDateEnd: dayjs().endOf("day").format("YYYY-MM-DD"),
+});
+
 // 浜у搧琛ㄥ崟寮规鏁版嵁
 const productFormVisible = ref(false);
 const productOperationType = ref("");
@@ -613,6 +659,8 @@
     taxInclusiveTotalPrice: "",
     taxExclusiveTotalPrice: "",
     invoiceType: "",
+    warnNum: "",
+    isChecked: false,
   },
   productRules: {
     productId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
@@ -623,6 +671,7 @@
       { required: true, message: "璇疯緭鍏�", trigger: "blur" },
     ],
     taxRate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    warnNum: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
     taxInclusiveTotalPrice: [
       { required: true, message: "璇疯緭鍏�", trigger: "blur" },
     ],
@@ -630,6 +679,7 @@
       { required: true, message: "璇疯緭鍏�", trigger: "blur" },
     ],
     invoiceType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    isChecked: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
   },
 });
 const { productForm, productRules } = toRefs(productFormData);
@@ -640,6 +690,86 @@
   headers: { Authorization: "Bearer " + getToken() },
 });
 
+// 瀵煎叆鐩稿叧
+const importUploadRef = ref(null);
+const importUpload = reactive({
+  title: "瀵煎叆閲囪喘鍙拌处",
+  open: false,
+  url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import",
+  headers: { Authorization: "Bearer " + getToken() },
+  isUploading: false,
+  beforeUpload: file => {
+    const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
+    const isLt10M = file.size / 1024 / 1024 < 10;
+    if (!isExcel) {
+      proxy.$modal.msgError("涓婁紶鏂囦欢鍙兘鏄� xlsx/xls 鏍煎紡!");
+      return false;
+    }
+    if (!isLt10M) {
+      proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!");
+      return false;
+    }
+    return true;
+  },
+  onChange: (file, fileList) => {
+    // noop
+  },
+  onProgress: (event, file, fileList) => {
+    // noop
+  },
+  onSuccess: (response, file, fileList) => {
+    importUpload.isUploading = false;
+    if (response?.code === 200) {
+      proxy.$modal.msgSuccess("瀵煎叆鎴愬姛");
+      importUpload.open = false;
+      if (importUploadRef.value) {
+        importUploadRef.value.clearFiles?.();
+      }
+      getList();
+    } else {
+      proxy.$modal.msgError(response?.msg || "瀵煎叆澶辫触");
+    }
+  },
+  onError: () => {
+    importUpload.isUploading = false;
+    proxy.$modal.msgError("瀵煎叆澶辫触锛岃閲嶈瘯");
+  },
+});
+
+const handleImport = () => {
+  importUpload.title = "瀵煎叆閲囪喘鍙拌处";
+  importUpload.open = true;
+  importUpload.isUploading = false;
+  if (importUploadRef.value) {
+    importUploadRef.value.clearFiles?.();
+  }
+};
+
+// 涓嬭浇瀵煎叆妯℃澘锛堝鍚庣璺緞涓嶅悓锛屽彲鍦ㄦ澶勮皟鏁达級
+const downloadTemplate = () => {
+  proxy.download(
+    "/purchase/ledger/exportTemplate",
+    {},
+    "閲囪喘鍙拌处瀵煎叆妯℃澘.xlsx"
+  );
+};
+
+const submitImportFile = () => {
+  importUpload.isUploading = true;
+  proxy.$refs["importUploadRef"]?.submit?.();
+};
+
+const changeDaterange = value => {
+  if (value) {
+    searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
+    searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
+  } else {
+    searchForm.entryDateStart = undefined;
+    searchForm.entryDateEnd = undefined;
+  }
+  handleQuery();
+};
+
 const formattedNumber = (row, column, cellValue) => {
   return parseFloat(cellValue).toFixed(2);
 };
@@ -649,8 +779,106 @@
   page.current = 1;
   getList();
 };
+
+// 淇濆瓨妯℃澘
+const handleButtonClick = async () => {
+  // 妫�鏌ユā鏉垮悕绉版槸鍚︿负绌�
+  if (!templateName.value || templateName.value.trim() === "") {
+    ElMessage({
+      message: "璇疯緭鍏ユā鏉垮悕绉�",
+      type: "warning",
+    });
+    return;
+  }
+
+  // 濡傛灉鏄�滄柊澧炴ā鏉库�濓紙娌℃湁閫変腑宸叉湁妯℃澘锛夛紝鎵嶉渶瑕佸仛閲嶅悕鏍¢獙锛�
+  // 鑻ユ槸閫変腑宸叉湁妯℃澘鍚庝慨鏀癸紝鍒欏厑璁镐娇鐢ㄥ師鍚嶇О锛堣涓烘洿鏂帮級
+  if (!currentTemplateId.value) {
+    const isDuplicate = checkTemplateNameDuplicate(templateName.value);
+    if (isDuplicate) {
+      ElMessage({
+        message: "妯℃澘鍚嶇О宸插瓨鍦紝璇锋洿鎹㈡ā鏉垮悕绉�",
+        type: "warning",
+      });
+      return;
+    }
+  }
+
+  // 妫�鏌ヤ緵搴斿晢鏄惁閫夋嫨
+  if (!form.value.supplierId) {
+    ElMessage({
+      message: "璇峰厛閫夋嫨渚涘簲鍟�",
+      type: "warning",
+    });
+    return;
+  }
+
+  // 妫�鏌ユ槸鍚︽湁浜у搧鏁版嵁
+  if (!productData.value || productData.value.length === 0) {
+    ElMessage({
+      message: "璇峰厛娣诲姞浜у搧淇℃伅",
+      type: "warning",
+    });
+    return;
+  }
+
+  try {
+    let params = {
+      productData: proxy.HaveJson(productData.value),
+      supplierId: form.value.supplierId,
+      paymentMethod: form.value.paymentMethod,
+      recorderId: form.value.recorderId,
+      projectName: form.value.projectName,
+      templateName: templateName.value.trim(),
+    };
+    console.log(
+      "template params ===>",
+      params,
+      "currentTemplateId:",
+      currentTemplateId.value
+    );
+
+    // 濡傛灉 currentTemplateId 鏈夊�硷紝璇存槑褰撳墠鏄�滅紪杈戝凡鏈夋ā鏉库�� 鈫� 璋冪敤鏇存柊鎺ュ彛
+    // 鍚﹀垯涓衡�滄柊寤烘ā鏉库�� 鈫� 璋冪敤鏂板鎺ュ彛
+    let res;
+    if (currentTemplateId.value) {
+      res = await updatePurchaseTemplate({
+        id: currentTemplateId.value,
+        ...params,
+      });
+    } else {
+      res = await addPurchaseTemplate(params);
+    }
+
+    if (res && res.code === 200) {
+      ElMessage({
+        message: currentTemplateId.value ? "妯℃澘鏇存柊鎴愬姛" : "妯℃澘淇濆瓨鎴愬姛",
+        type: "success",
+      });
+      // 淇濆瓨鎴愬姛鍚庨噸鏂拌幏鍙栨ā鏉垮垪琛�
+      await getTemplateList();
+      // 娓呯┖妯℃澘鍚嶇О杈撳叆
+      templateName.value = "";
+      filterInputValue.value = "";
+      isTemplateNameDuplicate.value = false;
+      // 淇濆瓨/鏇存柊瀹屾垚鍚庯紝閲嶇疆褰撳墠妯℃澘ID
+      currentTemplateId.value = null;
+    } else {
+      ElMessage({
+        message: res?.msg || "妯℃澘淇濆瓨澶辫触",
+        type: "error",
+      });
+    }
+  } catch (error) {
+    console.error("淇濆瓨妯℃澘澶辫触:", error);
+    ElMessage({
+      message: "妯℃澘淇濆瓨澶辫触锛岃绋嶅悗閲嶈瘯",
+      type: "error",
+    });
+  }
+};
 // 瀛愯〃鍚堣鏂规硶
-const summarizeChildrenTable = (param) => {
+const summarizeChildrenTable = param => {
   return proxy.summarizeTable(
     param,
     [
@@ -668,18 +896,23 @@
     }
   );
 };
-const paginationChange = (obj) => {
+const paginationChange = obj => {
   page.current = obj.page;
   page.size = obj.limit;
   getList();
 };
 const getList = () => {
   tableLoading.value = true;
-  purchaseListPage({ ...searchForm.value, ...page })
-    .then((res) => {
+  const { entryDate, ...rest } = searchForm;
+  purchaseListPage({ ...rest, ...page })
+    .then(res => {
       tableLoading.value = false;
-      tableData.value = res.data.records;
-      tableData.value.map((item) => {
+      // tableData.value = res.data.records;
+      tableData.value = res.data.records.map(record => ({
+        ...record,
+      }));
+      // 鍒濆鍖栧瓙鏁版嵁鏁扮粍
+      tableData.value.forEach(item => {
         item.children = [];
       });
       total.value = res.data.total;
@@ -690,38 +923,43 @@
     });
 };
 // 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
+const handleSelectionChange = selection => {
   selectedRows.value = selection;
 };
-const productSelected = (selectedRows) => {
+const productSelected = selectedRows => {
   productSelectedRows.value = selectedRows;
 };
 const expandedRowKeys = ref([]);
 // 灞曞紑琛�
-const expandChange = (row, expandedRows) => {
+const expandChange = async (row, expandedRows) => {
   if (expandedRows.length > 0) {
     expandedRowKeys.value = [];
     try {
-      productList({ salesLedgerId: row.id, type: 2 }).then((res) => {
-        const index = tableData.value.findIndex((item) => item.id === row.id);
-        if (index > -1) {
-          tableData.value[index].children = res.data;
-        }
+      const res = await productList({ salesLedgerId: row.id, type: 2 });
+      const index = tableData.value.findIndex(item => item.id === row.id);
+      if (index > -1) {
+        tableData.value[index].children = res.data || [];
         expandedRowKeys.value.push(row.id);
-      });
+      }
     } catch (error) {
-      console.log(error);
+      console.error("鍔犺浇浜у搧鍒楄〃澶辫触:", error);
+      proxy.$modal.msgError("鍔犺浇浜у搧鍒楄〃澶辫触");
+      // 灞曞紑澶辫触鏃讹紝绉婚櫎灞曞紑鐘舵��
+      const index = expandedRows.findIndex(item => item.id === row.id);
+      if (index > -1) {
+        expandedRows.splice(index, 1);
+      }
     }
   } else {
     expandedRowKeys.value = [];
   }
 };
 // 涓昏〃鍚堣鏂规硶
-const summarizeMainTable = (param) => {
+const summarizeMainTable = param => {
   return proxy.summarizeTable(param, ["contractAmount"]);
 };
 // 瀛愯〃鍚堣鏂规硶
-const summarizeProTable = (param) => {
+const summarizeProTable = param => {
   return proxy.summarizeTable(param, [
     "taxInclusiveUnitPrice",
     "taxInclusiveTotalPrice",
@@ -729,41 +967,83 @@
   ]);
 };
 // 鎵撳紑寮规
-const openForm = (type, row) => {
+const openForm = async (type, row) => {
+  if (type === 'detail') {
+    detailRef.value.open(row.id);
+    return;
+  }
+  // 缂栬緫鏃舵鏌ュ叆搴撶姸鎬侊紝瀹屽叏鍏ュ簱鏃朵笉鑳界紪杈�
+  if (type === "edit" && row) {
+    if (row.stockInStatus === '瀹屽叏鍏ュ簱') {
+      proxy.$modal.msgWarning("瀹屽叏鍏ュ簱鐘舵�佺殑璁板綍涓嶈兘缂栬緫");
+      return;
+    }
+  }
+
+  await getTemplateList();
+  await userListNoPage().then(res => {
+    userList.value = res.data;
+  });
   operationType.value = type;
   form.value = {};
   productData.value = [];
   fileList.value = [];
-  if (operationType.value == "add") {
-    createPurchaseNo().then((res) => {
-      form.value.purchaseContractNumber = res.data;
-    });
-  }
-  userListNoPage().then((res) => {
-    userList.value = res.data;
-  });
-  getSalesNo().then((res) => {
-    salesContractList.value = res;
-  });
-  getOptions().then((res) => {
-    supplierList.value = res.data;
-  });
-  form.value.recorderId = userStore.id;
-  form.value.entryDate = getCurrentDate();
-  if (type === "edit") {
-    currentId.value = row.id;
-    getPurchaseById({ id: row.id, type: 2 }).then((res) => {
-      form.value = { ...res };
-      productData.value = form.value.productData;
-      if (form.value.salesLedgerFiles) {
-        fileList.value = form.value.salesLedgerFiles;
-      } else {
-        fileList.value = [];
+  templateName.value = "";
+  filterInputValue.value = "";
+  isTemplateNameDuplicate.value = false;
+  try {
+    // 骞惰鍔犺浇鍩虹鏁版嵁
+    const [salesRes, supplierRes] = await Promise.all([
+      getSalesNo(),
+      getOptions(),
+    ]);
+
+    salesContractList.value = salesRes || [];
+    // 渚涘簲鍟嗚繃婊ゅ嚭isWhite=0 鐨勬暟鎹�
+    supplierList.value = (supplierRes.data || []).filter(
+      item => item.isWhite === 0
+    );
+
+    form.value.entryDate = getCurrentDate();
+
+    if (type === "add") {
+      // 鏂板鏃剁敓鎴愰噰璐悎鍚屽彿
+      try {
+        const purchaseNoRes = await createPurchaseNo();
+        if (purchaseNoRes?.data) {
+          form.value.purchaseContractNumber = purchaseNoRes.data;
+        }
+      } catch (error) {
+        console.error("鐢熸垚閲囪喘鍚堝悓鍙峰け璐�:", error);
+        proxy.$modal.msgWarning("鐢熸垚閲囪喘鍚堝悓鍙峰け璐�");
       }
-    });
+    } else if (type === "edit" && row?.id) {
+      // 缂栬緫鏃跺姞杞芥暟鎹�
+      currentId.value = row.id;
+      try {
+        const purchaseRes = await getPurchaseById({ id: row.id, type: 2 });
+        form.value = { ...purchaseRes, stockInStatus: row.stockInStatus };
+        fileList.value = purchaseRes.storageBlobVOS || [];
+        // 浣跨敤 productList 鎺ュ彛鑾峰彇浜у搧鍒楄〃锛屼互鑾峰彇鍏ュ簱瀹℃牳鐘舵��
+        const productRes = await productList({ salesLedgerId: row.id, type: 2 });
+        productData.value = productRes.data || [];
+      } catch (error) {
+        console.error("鍔犺浇閲囪喘鍙拌处鏁版嵁澶辫触:", error);
+        proxy.$modal.msgError("鍔犺浇鏁版嵁澶辫触");
+        return;
+      }
+    }
+
+    if (form.value.salesLedgerId == -1) {
+      form.value.salesLedgerId = null;
+    }
+    dialogFormVisible.value = true;
+  } catch (error) {
+    console.error("鎵撳紑琛ㄥ崟澶辫触:", error);
+    proxy.$modal.msgError("鍔犺浇鍩虹鏁版嵁澶辫触");
   }
-  dialogFormVisible.value = true;
 };
+
 // 涓婁紶鍓嶆牎妫�
 function handleBeforeUpload(file) {
   // 鏍℃鏂囦欢澶у皬
@@ -774,11 +1054,13 @@
   proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
   return true;
 }
+
 // 涓婁紶澶辫触
 function handleUploadError(err) {
   proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
   proxy.$modal.closeLoading();
 }
+
 // 涓婁紶鎴愬姛鍥炶皟
 function handleUploadSuccess(res, file, uploadFiles) {
   proxy.$modal.closeLoading();
@@ -790,38 +1072,62 @@
     proxy.$refs.fileUpload.handleRemove(file);
   }
 }
+
 // 绉婚櫎鏂囦欢
-function handleRemove(file) {
-  console.log("handleRemove", file.id);
-  if (file.size > 1024 * 1024 * 10) { 
-    // 浠呭墠绔竻鐞嗭紝涓嶈皟鐢ㄥ垹闄ゆ帴鍙e拰鎻愮ず
-    return; 
+async function handleRemove(file) {
+  if (!file?.id) {
+    return;
   }
-  if (operationType.value === "edit") {
-    let ids = [];
-    ids.push(file.id);
-    delLedgerFile(ids).then((res) => {
+  console.log("handleRemove", file.id);
+  if (file.size > 1024 * 1024 * 10) {
+    // 浠呭墠绔竻鐞嗭紝涓嶈皟鐢ㄥ垹闄ゆ帴鍙e拰鎻愮ず
+    return;
+  }
+
+  if (operationType.value === "edit" && file.id) {
+    try {
+      await delLedgerFile([file.id]);
       proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-    });
+    } catch (error) {
+      console.error("鍒犻櫎鏂囦欢澶辫触:", error);
+      proxy.$modal.msgError("鍒犻櫎鏂囦欢澶辫触");
+    }
   }
 }
+
 // 鎻愪氦琛ㄥ崟
 const submitForm = () => {
-  proxy.$refs["formRef"].validate((valid) => {
+  proxy.$refs["formRef"].validate(valid => {
     if (valid) {
       if (productData.value.length > 0) {
-        form.value.productData = proxy.HaveJson(productData.value);
+        // 鏂板鏃讹紝闇�瑕佷粠姣忎釜浜у搧瀵硅薄涓垹闄� id 瀛楁
+        let processedProductData = productData.value;
+        if (operationType.value === "add") {
+          processedProductData = productData.value.map(product => {
+            const { id, ...rest } = product;
+            return rest;
+          });
+        }
+        form.value.productData = proxy.HaveJson(processedProductData);
       } else {
         proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
         return;
       }
-      let tempFileIds = [];
-      if (fileList.value.length > 0) {
-        tempFileIds = fileList.value.map((item) => item.tempId);
-      }
-      form.value.tempFileIds = tempFileIds;
+      form.value.storageBlobDTOS = fileList.value;
       form.value.type = 2;
-      addOrEditPurchase(form.value).then((res) => {
+
+      // 濡傛灉salesLedgerId涓虹┖锛屽垯涓嶄紶閫抯alesContractNo
+      if (!form.value.salesLedgerId) {
+        form.value.salesContractNo = "";
+      }
+
+      // 鏂板鏃朵笉浼犻�抜d
+      const submitData = { ...form.value };
+      if (operationType.value === "add") {
+        delete submitData.id;
+      }
+
+      addOrEditPurchase(submitData).then(res => {
         proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
         closeDia();
         getList();
@@ -835,30 +1141,119 @@
   dialogFormVisible.value = false;
 };
 // 鎵撳紑浜у搧寮规
-const openProductForm = (type, row, index) => {
+const openProductForm = async (type, row, index) => {
+  // 缂栬緫鏃舵鏌ヤ骇鍝佸叆搴撳鏍哥姸鎬侊紝瀹屽叏鍏ュ簱鏃朵笉鑳界紪杈�
+  if (type === "edit" && row && row.stockInApprovalStatus === '瀹屽叏鍏ュ簱') {
+    proxy.$modal.msgWarning("瀹屽叏鍏ュ簱鐘舵�佺殑浜у搧涓嶈兘缂栬緫");
+    return;
+  }
+
   productOperationType.value = type;
   productOperationIndex.value = index;
   productForm.value = {};
   proxy.resetForm("productFormRef");
-  if (type === "edit") {
-    productForm.value = { ...row };
-  }
   productFormVisible.value = true;
-  getProductOptions();
+
+  // 鍏堣幏鍙栦骇鍝侀�夐」锛岀‘淇濇暟鎹姞杞藉畬鎴�
+  await getProductOptions();
+
+  // 绛夊緟 DOM 鏇存柊
+  await nextTick();
+
+  if (type === "add") {
+    productForm.value.isChecked = false;
+  }
+
+  if (type === "edit") {
+    // 澶嶅埗琛屾暟鎹�
+    productForm.value = { ...row };
+
+    // 濡傛灉鏄粠妯℃澘鍔犺浇鐨勬暟鎹紝鍙兘娌℃湁 productId 鍜� productModelId
+    // 闇�瑕佹牴鎹� productCategory 鍜� specificationModel 鏉ユ煡鎵惧搴旂殑 ID
+    if (!productForm.value.productId && productForm.value.productCategory) {
+      // 鏍规嵁 productCategory 鏌ユ壘 productId
+      const findProductIdByCategory = (nodes, categoryName) => {
+        for (let i = 0; i < nodes.length; i++) {
+          if (nodes[i].label === categoryName) {
+            return nodes[i].value;
+          }
+          if (nodes[i].children && nodes[i].children.length > 0) {
+            const found = findProductIdByCategory(
+              nodes[i].children,
+              categoryName
+            );
+            if (found) return found;
+          }
+        }
+        return null;
+      };
+
+      const productId = findProductIdByCategory(
+        productOptions.value,
+        productForm.value.productCategory
+      );
+      if (productId) {
+        productForm.value.productId = productId;
+        // 鑾峰彇鍨嬪彿鍒楄〃骞剁瓑寰呭畬鎴�
+        const modelRes = await modelList({ id: productId });
+        modelOptions.value = modelRes;
+
+        // 绛夊緟 DOM 鏇存柊
+        await nextTick();
+
+        // 鏍规嵁 specificationModel 鏌ユ壘 productModelId
+        if (
+          productForm.value.specificationModel &&
+          modelOptions.value.length > 0
+        ) {
+          const modelItem = modelOptions.value.find(
+            item => item.model === productForm.value.specificationModel
+          );
+          if (modelItem) {
+            productForm.value.productModelId = modelItem.id;
+            // 璁剧疆瑙勬牸鍨嬪彿鍜屽崟浣�
+            getProductModel(modelItem.id);
+          }
+        }
+      }
+    } else if (productForm.value.productId) {
+      // 濡傛灉鏈� productId锛屾甯稿姞杞藉瀷鍙峰垪琛�
+      await getModels(productForm.value.productId);
+
+      // 绛夊緟 DOM 鏇存柊
+      await nextTick();
+
+      if (productForm.value.productModelId) {
+        getProductModel(productForm.value.productModelId);
+      }
+    }
+
+    // 鏈�鍚庡啀绛夊緟涓�娆� DOM 鏇存柊锛岀‘淇濇墍鏈夋暟鎹兘宸茶缃�
+    await nextTick();
+  }
 };
 const getProductOptions = () => {
-  productTreeList().then((res) => {
+  return productTreeList().then(res => {
     productOptions.value = convertIdToValue(res);
+    return res;
   });
 };
-const getModels = (value) => {
-  productForm.value.productCategory = findNodeById(productOptions.value, value);
-  modelList({ id: value }).then((res) => {
-    modelOptions.value = res;
-  });
+const getModels = value => {
+  if (value) {
+    productForm.value.productCategory =
+      findNodeById(productOptions.value, value) || "";
+    return modelList({ id: value }).then(res => {
+      modelOptions.value = res;
+      return res;
+    });
+  } else {
+    productForm.value.productCategory = "";
+    modelOptions.value = [];
+    return Promise.resolve([]);
+  }
 };
-const getProductModel = (value) => {
-  const index = modelOptions.value.findIndex((item) => item.id === value);
+const getProductModel = value => {
+  const index = modelOptions.value.findIndex(item => item.id === value);
   if (index !== -1) {
     productForm.value.specificationModel = modelOptions.value[index].model;
     productForm.value.unit = modelOptions.value[index].unit;
@@ -870,19 +1265,20 @@
 const findNodeById = (nodes, productId) => {
   for (let i = 0; i < nodes.length; i++) {
     if (nodes[i].value === productId) {
-      return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣
+      return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣鐨刲abel
     }
     if (nodes[i].children && nodes[i].children.length > 0) {
       const foundNode = findNodeById(nodes[i].children, productId);
       if (foundNode) {
-        return foundNode.label; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖璇ヨ妭鐐�
+        return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝鐩存帴杩斿洖锛堝凡缁忔槸label瀛楃涓诧級
       }
     }
   }
   return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
 };
+
 function convertIdToValue(data) {
-  return data.map((item) => {
+  return data.map(item => {
     const { id, children, ...rest } = item;
     const newItem = {
       ...rest,
@@ -895,9 +1291,10 @@
     return newItem;
   });
 }
+
 // 鎻愪氦浜у搧琛ㄥ崟
 const submitProduct = () => {
-  proxy.$refs["productFormRef"].validate((valid) => {
+  proxy.$refs["productFormRef"].validate(valid => {
     if (valid) {
       if (operationType.value === "edit") {
         submitProductEdit();
@@ -918,11 +1315,12 @@
 const submitProductEdit = () => {
   productForm.value.salesLedgerId = currentId.value;
   productForm.value.type = 2;
-  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
+  addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
     proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
     closeProductDia();
-    getPurchaseById({ id: currentId.value, type: 2 }).then((res) => {
-      productData.value = res.productData;
+    // 浣跨敤 productList 鎺ュ彛鍒锋柊浜у搧鍒楄〃锛屼互鑾峰彇鍏ュ簱瀹℃牳鐘舵��
+    productList({ salesLedgerId: currentId.value, type: 2 }).then(res => {
+      productData.value = res.data || [];
     });
   });
 };
@@ -932,19 +1330,23 @@
     proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
     return;
   }
+  // 妫�鏌ラ�変腑鐨勪骇鍝佷腑鏄惁鏈夊畬鍏ㄥ叆搴撶殑
+  const hasFullyStocked = productSelectedRows.value.some(
+    row => row.stockInApprovalStatus === '瀹屽叏鍏ュ簱'
+  );
+  if (hasFullyStocked) {
+    proxy.$modal.msgWarning("閫変腑鐨勪骇鍝佷腑鍖呭惈瀹屽叏鍏ュ簱鐨勪骇鍝侊紝鏃犳硶鍒犻櫎");
+    return;
+  }
   if (operationType.value === "add") {
-    productSelectedRows.value.forEach((selectedRow) => {
-      const index = productData.value.findIndex(
-        (product) => product.id === selectedRow.id
-      );
-      if (index !== -1) {
-        productData.value.splice(index, 1);
-      }
-    });
+    productData.value = productData.value.filter(
+      item => !productSelectedRows.value.includes(item)
+    );
+    productSelectedRows.value = [];
   } else {
     let ids = [];
     if (productSelectedRows.value.length > 0) {
-      ids = productSelectedRows.value.map((item) => item.id);
+      ids = productSelectedRows.value.map(item => item.id);
     }
     ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
       confirmButtonText: "纭",
@@ -952,14 +1354,13 @@
       type: "warning",
     })
       .then(() => {
-        delProduct(ids).then((res) => {
+        delProduct(ids).then(res => {
           proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
           closeProductDia();
-          getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
-            (res) => {
-              productData.value = res.productData;
-            }
-          );
+          // 浣跨敤 productList 鎺ュ彛鍒锋柊浜у搧鍒楄〃锛屼互鑾峰彇鍏ュ簱瀹℃牳鐘舵��
+          productList({ salesLedgerId: currentId.value, type: 2 }).then(res => {
+            productData.value = res.data || [];
+          });
         });
       })
       .catch(() => {
@@ -990,24 +1391,20 @@
 const handleDelete = () => {
   let ids = [];
   if (selectedRows.value.length > 0) {
-		// 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
-		const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName);
-		if (unauthorizedData.length > 0) {
-			proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
-			return;
-		}
-    ids = selectedRows.value.map((item) => item.id);
+    ids = selectedRows.value
+      .filter(item => item.salesLedgerId === null)
+      .map(item => item.id);
   } else {
     proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
     return;
   }
-  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
     confirmButtonText: "纭",
     cancelButtonText: "鍙栨秷",
     type: "warning",
   })
     .then(() => {
-      delPurchase(ids).then((res) => {
+      delPurchase(ids).then(res => {
         proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
         getList();
       });
@@ -1016,6 +1413,7 @@
       proxy.$modal.msg("宸插彇娑�");
     });
 };
+
 // 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
 function getCurrentDate() {
   const today = new Date();
@@ -1024,11 +1422,12 @@
   const day = String(today.getDate()).padStart(2, "0");
   return `${year}-${month}-${day}`;
 }
+
 const mathNum = () => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
+  if (!productForm.value.taxRate) {
+    proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+    return;
+  }
   if (!productForm.value.taxInclusiveUnitPrice) {
     return;
   }
@@ -1050,50 +1449,97 @@
       );
   }
 };
-const reverseMathNum = (field) => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
+const reverseMathNum = field => {
+  if (!productForm.value.taxRate) {
+    proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+    return;
+  }
   const taxRate = Number(productForm.value.taxRate);
   if (!taxRate) return;
-  if (field === 'taxInclusiveTotalPrice') {
+
+  // 纭繚杈撳叆鍊间笉涓鸿礋鏁�
+  if (
+    field === "taxInclusiveTotalPrice" ||
+    field === "taxExclusiveTotalPrice"
+  ) {
+    const value = Number(productForm.value[field]);
+    if (value < 0) {
+      productForm.value[field] = "0";
+      proxy.$modal.msgWarning("鍊间笉鑳藉皬浜�0");
+      return;
+    }
+  }
+
+  if (field === "taxInclusiveTotalPrice") {
     // 宸茬煡鍚◣鎬讳环鍜屾暟閲忥紝鍙嶇畻鍚◣鍗曚环
     if (productForm.value.quantity) {
-      productForm.value.taxInclusiveUnitPrice = 
-        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
+      productForm.value.taxInclusiveUnitPrice = (
+        Number(productForm.value.taxInclusiveTotalPrice) /
+        Number(productForm.value.quantity)
+      ).toFixed(2);
+      // 纭繚缁撴灉涓嶄负璐熸暟
+      if (Number(productForm.value.taxInclusiveUnitPrice) < 0) {
+        productForm.value.taxInclusiveUnitPrice = "0";
+      }
     }
     // 宸茬煡鍚◣鎬讳环鍜屽惈绋庡崟浠凤紝鍙嶇畻鏁伴噺
     else if (productForm.value.taxInclusiveUnitPrice) {
-      productForm.value.quantity = 
-        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
+      productForm.value.quantity = (
+        Number(productForm.value.taxInclusiveTotalPrice) /
+        Number(productForm.value.taxInclusiveUnitPrice)
+      ).toFixed(2);
+      // 纭繚缁撴灉涓嶄负璐熸暟
+      if (Number(productForm.value.quantity) < 0) {
+        productForm.value.quantity = "0";
+      }
     }
     // 鍙嶇畻涓嶅惈绋庢�讳环
-    productForm.value.taxExclusiveTotalPrice = 
-      (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2);
-  } else if (field === 'taxExclusiveTotalPrice') {
+    productForm.value.taxExclusiveTotalPrice = (
+      Number(productForm.value.taxInclusiveTotalPrice) /
+      (1 + taxRate / 100)
+    ).toFixed(2);
+    // 纭繚缁撴灉涓嶄负璐熸暟
+    if (Number(productForm.value.taxExclusiveTotalPrice) < 0) {
+      productForm.value.taxExclusiveTotalPrice = "0";
+    }
+  } else if (field === "taxExclusiveTotalPrice") {
     // 鍙嶇畻鍚◣鎬讳环
-    productForm.value.taxInclusiveTotalPrice = 
-      (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2);
+    productForm.value.taxInclusiveTotalPrice = (
+      Number(productForm.value.taxExclusiveTotalPrice) *
+      (1 + taxRate / 100)
+    ).toFixed(2);
+    // 纭繚缁撴灉涓嶄负璐熸暟
+    if (Number(productForm.value.taxInclusiveTotalPrice) < 0) {
+      productForm.value.taxInclusiveTotalPrice = "0";
+    }
     // 宸茬煡鏁伴噺锛屽弽绠楀惈绋庡崟浠�
     if (productForm.value.quantity) {
-      productForm.value.taxInclusiveUnitPrice = 
-        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
+      productForm.value.taxInclusiveUnitPrice = (
+        Number(productForm.value.taxInclusiveTotalPrice) /
+        Number(productForm.value.quantity)
+      ).toFixed(2);
+      // 纭繚缁撴灉涓嶄负璐熸暟
+      if (Number(productForm.value.taxInclusiveUnitPrice) < 0) {
+        productForm.value.taxInclusiveUnitPrice = "0";
+      }
     }
     // 宸茬煡鍚◣鍗曚环锛屽弽绠楁暟閲�
     else if (productForm.value.taxInclusiveUnitPrice) {
-      productForm.value.quantity = 
-        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
+      productForm.value.quantity = (
+        Number(productForm.value.taxInclusiveTotalPrice) /
+        Number(productForm.value.taxInclusiveUnitPrice)
+      ).toFixed(2);
+      // 纭繚缁撴灉涓嶄负璐熸暟
+      if (Number(productForm.value.quantity) < 0) {
+        productForm.value.quantity = "0";
+      }
     }
   }
 };
 // 閿�鍞悎鍚岄�夋嫨鏀瑰彉鏂规硶
-const salesLedgerChange = async (row) => {
-  console.log("row", row);
-  var index = salesContractList.value.findIndex((item) => item.id == row);
-  console.log("index", index);
+const salesLedgerChange = async row => {
+  var index = salesContractList.value.findIndex(item => item.id == row);
   if (index > -1) {
-    form.value.projectName = salesContractList.value[index].projectName;
     await querygProductInfoByContractNo();
   }
 };
@@ -1107,9 +1553,198 @@
   }
 };
 
+// 鑾峰彇妯℃澘淇℃伅
+const getTemplateList = async () => {
+  let res = await getPurchaseTemplateList();
+  if (res && res.code === 200 && Array.isArray(res.data)) {
+    templateList.value = res.data;
+  }
+};
+
+// 鎵撳紑闄勪欢寮规
+const openFileDialog = async row => {
+  recordId.value = row.id;
+  fileListDialogVisible.value = true;
+};
+
+// 鍒犻櫎妯℃澘
+const handleDeleteTemplate = async item => {
+  if (!item.id) {
+    proxy.$modal.msgWarning("鏃犳硶鍒犻櫎璇ユā鏉�");
+    return;
+  }
+
+  try {
+    await ElMessageBox.confirm(
+      `纭畾瑕佸垹闄ゆā鏉�"${item.templateName}"鍚楋紵`,
+      "鍒犻櫎纭",
+      {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      }
+    );
+
+    const res = await delPurchaseTemplate([item.id]);
+    if (res && res.code === 200) {
+      ElMessage({
+        message: "鍒犻櫎鎴愬姛",
+        type: "success",
+      });
+      // 濡傛灉鍒犻櫎鐨勬槸褰撳墠閫変腑鐨勬ā鏉匡紝娓呯┖閫夋嫨
+      if (templateName.value === item.templateName) {
+        templateName.value = "";
+        filterInputValue.value = "";
+      }
+      // 閲嶆柊鑾峰彇妯℃澘鍒楄〃
+      await getTemplateList();
+    } else {
+      ElMessage({
+        message: res?.msg || "鍒犻櫎澶辫触",
+        type: "error",
+      });
+    }
+  } catch (error) {
+    if (error !== "cancel") {
+      console.error("鍒犻櫎妯℃澘澶辫触:", error);
+      ElMessage({
+        message: "鍒犻櫎澶辫触锛岃绋嶅悗閲嶈瘯",
+        type: "error",
+      });
+    }
+  }
+};
+
 onMounted(() => {
   getList();
+  getTemplateList();
 });
 </script>
 
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+.invalid-row {
+  opacity: 0.6;
+  background-color: #f5f7fa;
+}
+
+.el-row {
+  justify-content: space-between;
+  align-items: center;
+}
+
+.no-arrow-select {
+  --el-select-suffix-icon-color: transparent;
+  /* 闅愯棌榛樿涓嬫媺绠ご */
+}
+
+.readonly-text {
+  display: inline-block;
+  width: 100%;
+  padding: 0 15px;
+  height: 32px;
+  line-height: 32px;
+  background-color: var(--el-fill-color-light);
+  border-radius: var(--el-border-radius-base);
+  color: var(--el-text-color-regular);
+  font-size: var(--el-font-size-base);
+  box-sizing: border-box;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.readonly-text-area {
+  width: 100%;
+  min-height: 52px;
+  padding: 5px 15px;
+  background-color: var(--el-fill-color-light);
+  border-radius: var(--el-border-radius-base);
+  color: var(--el-text-color-regular);
+  font-size: var(--el-font-size-base);
+  box-sizing: border-box;
+  white-space: pre-wrap;
+  word-break: break-all;
+}
+
+.detail-file-list {
+  width: 100%;
+  padding: 10px 15px;
+  background-color: var(--el-fill-color-light);
+  border-radius: var(--el-border-radius-base);
+}
+
+.detail-file-item {
+  line-height: 24px;
+  margin-bottom: 5px;
+}
+
+.detail-file-item:last-child {
+  margin-bottom: 0;
+}
+
+.select-button-group {
+  display: flex;
+  align-items: center;
+}
+
+// 瀹℃壒浜鸿妭鐐瑰鍣ㄦ牱寮�
+.approver-nodes-container {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16px;
+  padding: 16px;
+  background-color: #f8f9fa;
+  border-radius: 4px;
+  border: 1px solid #e4e7ed;
+}
+
+.approver-node-item {
+  flex: 0 0 calc(33.333% - 12px);
+  min-width: 200px;
+  padding: 12px;
+  background-color: #fff;
+  border-radius: 4px;
+  border: 1px solid #dcdfe6;
+  transition: all 0.3s;
+
+  &:hover {
+    border-color: #409eff;
+    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
+  }
+}
+
+.approver-node-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 8px;
+}
+
+.approver-node-label {
+  font-size: 13px;
+  font-weight: 500;
+  color: #606266;
+}
+
+@media (max-width: 1200px) {
+  .approver-node-item {
+    flex: 0 0 calc(50% - 8px);
+  }
+}
+
+@media (max-width: 768px) {
+  .approver-node-item {
+    flex: 0 0 100%;
+  }
+}
+
+// 鍒犻櫎鍥炬爣鏍峰紡
+.delete-icon {
+  transition: all 0.3s;
+
+  &:hover {
+    color: #f56c6c !important;
+    transform: scale(1.2);
+  }
+}
+</style>

--
Gitblit v1.9.3