From 20e5250ea7a7baeb92a63e9bb8150051fe2e011d Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期一, 14 九月 2026 15:32:40 +0800
Subject: [PATCH] feat: 付款申请创建采购付款分栏与财务审核权限、工资表银行账号与卡号识别银行、采购台账移除采购油品

---
 src/views/procurementManagement/procurementLedger/index.vue            |   26 ----
 src/views/personnelManagement/monthlyStatistics/components/formDia.vue |  127 +++++++++++++++++++++++++
 src/views/personnelManagement/monthlyStatistics/index.vue              |    1 
 src/views/financialManagement/payable/paymentApply.vue                 |  116 +++++++++++++++++++++++
 4 files changed, 247 insertions(+), 23 deletions(-)

diff --git a/src/views/financialManagement/payable/paymentApply.vue b/src/views/financialManagement/payable/paymentApply.vue
index 309d6d3..75d2029 100644
--- a/src/views/financialManagement/payable/paymentApply.vue
+++ b/src/views/financialManagement/payable/paymentApply.vue
@@ -73,7 +73,7 @@
         <template #operation="{ row }">
           <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
           <el-button type="primary" link @click="edit(row)" v-if="isPendingStatus(row.status)">缂栬緫</el-button>
-          <el-button type="success" link @click="handleAudit(row)" v-if="isPendingStatus(row.status)">瀹℃牳</el-button>
+          <el-button type="success" link :disabled="!canAudit(row)" @click="handleAudit(row)" v-if="isPendingStatus(row.status)">瀹℃牳</el-button>
           <el-button type="warning" link @click="openPaymentDialog(row)" v-if="isApprovedStatus(row.status)">浠樻</el-button>
           <el-button type="danger" link @click="handleDelete(row)" v-if="isPendingStatus(row.status)">鍒犻櫎</el-button>
         </template>
@@ -213,6 +213,52 @@
         <el-form-item label="澶囨敞" prop="remark">
           <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ娉�" :disabled="isView" />
         </el-form-item>
+        <div class="form-section-title">鍒涘缓閲囪喘浠樻</div>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="娆鹃」鐢ㄩ��" prop="paymentPurpose">
+              <el-select v-model="form.paymentPurpose" placeholder="璇烽�夋嫨娆鹃」鐢ㄩ��" clearable style="width: 100%" :disabled="isView">
+                <el-option label="閲囪喘璐ф" value="閲囪喘璐ф" />
+                <el-option label="杩愯垂" value="杩愯垂" />
+                <el-option label="鎶奸噾" value="鎶奸噾" />
+                <el-option label="鏈嶅姟璐�" value="鏈嶅姟璐�" />
+                <el-option label="鍏朵粬" value="鍏朵粬" />
+              </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 :disabled="isView" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="缁忓姙浜�" prop="operatorName">
+              <el-input v-model="form.operatorName" placeholder="璇疯緭鍏ョ粡鍔炰汉" clearable :disabled="isView" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璐㈠姟瀹℃牳" prop="financeAuditorId">
+              <el-select
+                v-model="form.financeAuditorId"
+                placeholder="璇烽�夋嫨瀹℃牳浜哄憳"
+                clearable
+                filterable
+                style="width: 100%"
+                :disabled="isView"
+                @change="handleFinanceAuditorChange"
+              >
+                <el-option
+                  v-for="item in userList"
+                  :key="item.userId"
+                  :label="item.nickName"
+                  :value="item.userId"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
       <template v-if="!isView" #footer>
         <el-button type="primary" :loading="submitLoading" @click="submitForm">纭畾</el-button>
@@ -368,6 +414,8 @@
   deleteAccountPaymentApplication,
 } from "@/api/financialManagement/accountPaymentApplication.js";
 import { addAccountPurchasePayment } from "@/api/financialManagement/accountPurchasePayment.js";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
+import useUserStore from "@/store/modules/user";
 
 defineOptions({
   name: "浠樻鐢宠",
@@ -375,6 +423,7 @@
 
 const { proxy } = getCurrentInstance();
 const { checkout_payment } = proxy.useDict("checkout_payment");
+const userStore = useUserStore();
 
 const filters = reactive({
   invoiceApplicationNo: "",
@@ -395,6 +444,8 @@
   { label: "浠樻閲戦", prop: "amount", dataType: "slot", slot: "amount" },
   { label: "浠樻鏂瑰紡", prop: "paymentMethod", dataType: "slot", slot: "paymentMethod", width: "120" },
   { label: "鐢宠鏃ユ湡", prop: "applyDate", width: "120" },
+  { label: "娆鹃」鐢ㄩ��", prop: "paymentPurpose", width: "120" },
+  { label: "璐㈠姟瀹℃牳", prop: "financeAuditor", width: "120" },
   { label: "鐘舵��", prop: "status", dataType: "slot", slot: "status", width: "100" },
   { label: "鎿嶄綔", prop: "operation", dataType: "slot", slot: "operation", width: "260", fixed: "right" },
 ];
@@ -409,6 +460,7 @@
 const submitLoading = ref(false);
 const currentId = ref(null);
 const supplierList = ref([]);
+const userList = ref([]);
 
 const inboundBatchList = ref([]);
 const inboundBatchOptions = ref([]);
@@ -457,6 +509,11 @@
   inboundBatches: "",
   status: 0,
   createTime: "",
+  paymentPurpose: "",
+  projectName: "",
+  operatorName: "",
+  financeAuditor: "",
+  financeAuditorId: null,
 });
 const formCreateTimeDate = computed({
   get: () => (form.createTime ? String(form.createTime).split(" ")[0] : ""),
@@ -477,6 +534,10 @@
   paymentAmount: [{ required: true, message: "璇疯緭鍏ヤ粯娆鹃噾棰�", trigger: "blur" }],
   paymentMethod: [{ required: true, message: "璇烽�夋嫨浠樻鏂瑰紡", trigger: "change" }],
   applyDate: [{ required: true, message: "璇烽�夋嫨鐢宠鏃ユ湡", trigger: "change" }],
+  paymentPurpose: [{ required: true, message: "璇烽�夋嫨娆鹃」鐢ㄩ��", trigger: "change" }],
+  projectName: [{ required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" }],
+  operatorName: [{ required: true, message: "璇疯緭鍏ョ粡鍔炰汉", trigger: "blur" }],
+  financeAuditorId: [{ required: true, message: "璇烽�夋嫨璐㈠姟瀹℃牳浜哄憳", trigger: "change" }],
 };
 
 const getSummaries = ({ columns, data }) => {
@@ -509,6 +570,15 @@
 };
 
 const isPendingStatus = (status) => normalizeStatus(status) === 0;
+
+// 瀹℃牳鏉冮檺锛氭寚瀹氫簡璐㈠姟瀹℃牳浜烘椂浠呮湰浜哄彲瀹℃牳锛涙湭鎸囧畾鏃朵繚鎸佸師閫昏緫锛堣皝閮借兘瀹★級
+const canAudit = (row) => {
+  const auditorId = row.financeAuditorId;
+  if (auditorId == null || auditorId === "") {
+    return true;
+  }
+  return Number(userStore.id) === Number(auditorId);
+};
 
 const isApprovedStatus = (status) => normalizeStatus(status) === 1;
 
@@ -610,6 +680,11 @@
 
 const fillFormFromRow = (row) => {
   const stockInRecordIds = parseStockInRecordIds(row.stockInRecordIds);
+  // 瀹℃牳浜哄凡涓嶅湪鐢ㄦ埛鍒楄〃鏃讹紝鐢ㄥ凡淇濆瓨鐨勫鍚嶈ˉ杩涗笅鎷夛紝淇濊瘉鏌ョ湅/缂栬緫鏄剧ず鍚嶅瓧鑰岄潪瑁窱D
+  const auditorId = row.financeAuditorId ?? null;
+  if (auditorId != null && !userList.value.some((u) => u.userId === auditorId)) {
+    userList.value.push({ userId: auditorId, nickName: row.financeAuditor ?? `鐢ㄦ埛${auditorId}` });
+  }
   Object.assign(form, {
     invoiceApplicationNo: row.invoiceApplicationNo ?? row.applyCode ?? "",
     supplierId: row.supplierId,
@@ -622,6 +697,11 @@
     inboundBatches: formatInboundBatches(row.inboundBatches),
     status: normalizeStatus(row.status),
     createTime: row.createTime ?? "",
+    paymentPurpose: row.paymentPurpose ?? "",
+    projectName: row.projectName ?? "",
+    operatorName: row.operatorName ?? "",
+    financeAuditor: row.financeAuditor ?? "",
+    financeAuditorId: row.financeAuditorId ?? null,
   });
 };
 
@@ -654,6 +734,11 @@
     status: 0,
     paymentAmount: form.paymentAmount,
     createTime: form.createTime,
+    paymentPurpose: form.paymentPurpose || "",
+    projectName: form.projectName || "",
+    operatorName: form.operatorName || "",
+    financeAuditor: form.financeAuditor || "",
+    financeAuditorId: form.financeAuditorId ?? null,
   };
   if (forUpdate) {
     payload.id = currentId.value;
@@ -666,6 +751,18 @@
     if (res.code === 200) {
       supplierList.value = res.data ?? [];
     }
+  });
+};
+
+// 璐㈠姟瀹℃牳浜哄憳閫夋嫨锛氱粦瀹氱郴缁熺敤鎴凤紝閫変腑鍚庡甫鍑哄鍚�
+const handleFinanceAuditorChange = (userId) => {
+  const user = userList.value.find((item) => item.userId === userId);
+  form.financeAuditor = user?.nickName ?? "";
+};
+
+const getUserList = () => {
+  userListNoPageByTenantId().then((res) => {
+    userList.value = res.data ?? [];
   });
 };
 
@@ -765,6 +862,11 @@
     inboundBatches: "",
     status: 0,
     createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+    paymentPurpose: "",
+    projectName: "",
+    operatorName: "",
+    financeAuditor: "",
+    financeAuditorId: null,
   });
   inboundBatchList.value = [];
   inboundBatchOptions.value = [];
@@ -1058,11 +1160,23 @@
 
 onMounted(() => {
   getSupplierList();
+  getUserList();
   getTableData();
 });
 </script>
 
 <style lang="scss" scoped>
+.form-section-title {
+  margin: 18px 0 14px;
+  padding: 8px 14px;
+  font-size: 15px;
+  font-weight: 600;
+  color: #1d6fd8;
+  background: linear-gradient(90deg, rgba(64, 158, 255, 0.14), rgba(64, 158, 255, 0.02));
+  border-left: 4px solid #409eff;
+  border-radius: 3px;
+}
+
 .actions {
   display: flex;
   justify-content: space-between;
diff --git a/src/views/personnelManagement/monthlyStatistics/components/formDia.vue b/src/views/personnelManagement/monthlyStatistics/components/formDia.vue
index 46515ed..ac39ae4 100644
--- a/src/views/personnelManagement/monthlyStatistics/components/formDia.vue
+++ b/src/views/personnelManagement/monthlyStatistics/components/formDia.vue
@@ -104,6 +104,17 @@
                 </el-select>
               </el-form-item>
             </el-col>
+            <el-col :span="6">
+              <el-form-item label="閾惰璐﹀彿"
+                            prop="bankAccount">
+                <el-input v-model="form.bankAccount"
+                          placeholder="璇疯緭鍏�16-19浣嶉摱琛岃处鍙�"
+                          maxlength="19"
+                          clearable
+                          @input="handleBankAccountInput"
+                          @blur="handleBankAccountBlur" />
+              </el-form-item>
+            </el-col>
           </el-row>
         </el-form>
       </el-card>
@@ -512,6 +523,98 @@
     requestEmployeeRecalculate(row);
   };
 
+  // 閾惰璐﹀彿杈撳叆锛氬彧鍏佽鏁板瓧锛岃嚜鍔ㄥ墧闄ょ┖鏍�/妯嚎绛夛紝鏈�澶�19浣嶏紱杈撳叆鍒�16-19浣嶇珛鍗宠嚜鍔ㄨ瘑鍒摱琛�
+  const handleBankAccountInput = () => {
+    form.value.bankAccount = String(form.value.bankAccount ?? "")
+      .replace(/\D/g, "")
+      .slice(0, 19);
+    autoFillPayBank();
+  };
+
+  // 鍗″彿璇嗗埆閾惰骞跺洖濉�"鏀粯閾惰"涓嬫媺锛堣瘑鍒笉鍒颁笉澶勭悊锛屼笉褰卞搷鎻愪氦锛�
+  const autoFillPayBank = () => {
+    const value = form.value.bankAccount;
+    if (!/^\d{16,19}$/.test(value)) {
+      return;
+    }
+    const bankName = recognizeBankByCardNo(value);
+    if (!bankName) {
+      return;
+    }
+    if (bankOptions.value.includes(bankName)) {
+      if (form.value.payBank !== bankName) {
+        form.value.payBank = bankName;
+        proxy.$modal.msgSuccess(`宸茶嚜鍔ㄨ瘑鍒负锛�${bankName}`);
+      }
+    } else {
+      proxy.$modal.msg(`璇嗗埆鍒伴摱琛屽崱灞炰簬銆�${bankName}銆戯紝璇锋墜鍔ㄩ�夋嫨鏀粯閾惰`);
+    }
+  };
+
+  // 閾惰璐﹀彿 Luhn 鏍¢獙锛堜粎鐢ㄤ簬璀﹀憡鎻愮ず锛屼笉闃绘柇鎻愪氦锛�
+  const luhnValid = account => {
+    let sum = 0;
+    let alternate = false;
+    for (let i = account.length - 1; i >= 0; i--) {
+      let n = Number(account[i]);
+      if (alternate) {
+        n *= 2;
+        if (n > 9) n -= 9;
+      }
+      sum += n;
+      alternate = !alternate;
+    }
+    return sum % 10 === 0;
+  };
+
+  // 甯歌閾惰鍊熻鍗� BIN 鍓嶇紑琛紙鍓�6浣嶏紝浠呯敤浜庤嚜鍔ㄨ瘑鍒紝涓嶅仛寮烘牎楠岋級
+  const BANK_BINS = {
+    "涓浗宸ュ晢閾惰": ["620200", "621210", "621211", "621225", "621226", "621227", "621288", "621723", "622202", "622203"],
+    "涓浗寤鸿閾惰": ["621284", "621466", "621488", "621499", "621598", "621599", "621700", "622700"],
+    "涓浗鍐滀笟閾惰": ["621282", "621336", "622827", "622828", "622845", "622848", "622849"],
+    "涓浗閾惰": ["621660", "621661", "621662", "621663", "621665", "621666", "621667", "621668", "621669", "621725", "621756", "621790", "622760"],
+    "浜ら�氶摱琛�": ["622258", "622260", "622261", "622262"],
+    "鎷涘晢閾惰": ["621286", "621483", "621485", "621486", "622580", "622588"],
+    "涓浗閭斂鍌ㄨ搫閾惰": ["621095", "621799", "622150", "622151", "622188"],
+    "鍏翠笟閾惰": ["622908", "622909"],
+    "涓浗姘戠敓閾惰": ["621691", "622615", "622617", "622622"],
+    "涓浗鍏夊ぇ閾惰": ["622661", "622662", "622663"],
+    "涓俊閾惰": ["621771", "621773", "622690", "622698"],
+    "涓婃捣娴︿笢鍙戝睍閾惰": ["621792", "621793", "621795", "622521", "622522"],
+    "鍗庡閾惰": ["622630", "622631", "622632"],
+    "骞冲畨閾惰": ["621625", "621626", "622155", "622156"],
+    "骞垮彂閾惰": ["622568", "622569"],
+  };
+
+  // 鏍规嵁鍗″彿鍓�6浣嶈瘑鍒摱琛屽悕绉帮紙璇嗗埆涓嶅埌杩斿洖绌轰覆锛�
+  const recognizeBankByCardNo = account => {
+    if (!/^\d{16,19}$/.test(account)) {
+      return "";
+    }
+    const prefix6 = account.slice(0, 6);
+    for (const [bank, prefixes] of Object.entries(BANK_BINS)) {
+      if (prefixes.includes(prefix6)) {
+        return bank;
+      }
+    }
+    return "";
+  };
+
+  // 澶辩劍鏍¢獙锛氶暱搴�/鏍煎紡閿欒鐢辫〃鍗曡鍒欑孩鑹叉彁绀哄苟鎷︽埅锛汱uhn 涓嶉�氳繃浠呴粍鑹茶鍛婏紱鍐嶈瘑鍒竴娆¢摱琛�
+  const handleBankAccountBlur = () => {
+    const value = form.value.bankAccount;
+    if (!value) {
+      return;
+    }
+    if (!/^\d{16,19}$/.test(value)) {
+      return; // 绾㈣壊閿欒鐢辫〃鍗曡鍒欏鐞�
+    }
+    if (!luhnValid(value)) {
+      proxy.$modal.msgWarning("閾惰璐﹀彿 Luhn 鏍¢獙鏈�氳繃锛堜粎鎻愮ず锛屽彲缁х画鎻愪氦锛�");
+    }
+    autoFillPayBank();
+  };
+
   // 鍩虹璧勬枡琛ㄥ崟
   const data = reactive({
     form: {
@@ -522,6 +625,7 @@
       remark: "",
       payBank: "",
       auditUserId: undefined,
+      bankAccount: "",
     },
     rules: {
       salaryTitle: [
@@ -533,6 +637,20 @@
       ],
       auditUserId: [
         { required: true, message: "璇烽�夋嫨瀹℃牳浜�", trigger: "change" },
+      ],
+      bankAccount: [
+        {
+          validator: (rule, value, callback) => {
+            if (!value) {
+              return callback();
+            }
+            if (!/^\d{16,19}$/.test(value)) {
+              return callback(new Error("閾惰璐﹀彿蹇呴』涓�16-19浣嶇函鏁板瓧"));
+            }
+            callback();
+          },
+          trigger: "blur",
+        },
       ],
     },
   });
@@ -656,6 +774,7 @@
         remark: "",
         payBank: "",
         auditUserId: undefined,
+        bankAccount: "",
       });
       // 缂栬緫锛氬垪琛ㄩ〉宸茶繑鍥炰富琛ㄥ瓧娈碉紱杩欓噷鍙仛鍥炴樉锛堟槑缁嗙敱鈥滅敓鎴愬伐璧勮〃/璁$畻宸ヨ祫鈥濆緱鍒帮級
       if (type === "edit" && row?.id) {
@@ -672,6 +791,7 @@
         form.value.remark = row.remark ?? "";
         form.value.payBank = row.payBank ?? "";
         form.value.auditUserId = row.auditUserId ?? undefined;
+        form.value.bankAccount = row.bankAccount ?? "";
 
         // 濡傛灉鏈夊憳宸ユ槑缁嗘暟鎹紝鐩存帴鍙嶆樉
         if (row.staffSalaryDetailList && row.staffSalaryDetailList.length > 0) {
@@ -809,6 +929,12 @@
   };
 
   const saveData = status => {
+    // 鎻愪氦鍓嶅厹搴曟牎楠岄摱琛岃处鍙凤細闀垮害/鏍煎紡涓嶇鐩存帴鎷︽埅
+    const account = String(form.value.bankAccount ?? "");
+    if (account && !/^\d{16,19}$/.test(account)) {
+      proxy.$modal.msgError("閾惰璐﹀彿蹇呴』涓�16-19浣嶇函鏁板瓧锛岃淇敼鍚庡啀鎻愪氦");
+      return;
+    }
     const payload = {
       id: form.value.id,
       salaryTitle: form.value.salaryTitle,
@@ -818,6 +944,7 @@
       payBank: form.value.payBank,
       auditUserId: form.value.auditUserId,
       auditUserName: auditUserName.value,
+      bankAccount: account,
       totalSalary: totalSalary.value,
       staffSalaryDetailList: employeeList.value.map(e => ({
         staffOnJobId: e.staffOnJobId ?? e.staffId ?? e.id,
diff --git a/src/views/personnelManagement/monthlyStatistics/index.vue b/src/views/personnelManagement/monthlyStatistics/index.vue
index f581070..92b194d 100644
--- a/src/views/personnelManagement/monthlyStatistics/index.vue
+++ b/src/views/personnelManagement/monthlyStatistics/index.vue
@@ -149,6 +149,7 @@
   },
   { label: "宸ヨ祫鎬婚", prop: "totalSalary", width: 120 },
   { label: "鏀粯閾惰", prop: "payBank", width: 120 },
+  { label: "閾惰璐﹀彿", prop: "bankAccount", width: 180 },
   { label: "瀹℃牳浜�", prop: "auditUserName", width: 110 },
   { label: "澶囨敞", prop: "remark", minWidth: 120 },
   {
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index 087f716..717248d 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -424,24 +424,6 @@
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
-            <el-form-item label="閲囪喘娌瑰搧锛�"
-                          prop="purchaseOilProduct">
-              <el-select v-model="form.purchaseOilProduct"
-                         placeholder="璇烽�夋嫨"
-                         clearable
-                         style="width: 100%">
-                <el-option label="姹�#92"
-                           value="姹�#92" />
-                <el-option label="姹�#95"
-                           value="姹�#95" />
-                <el-option label="鏌�#0"
-                           value="鏌�#0" />
-                <el-option label="鏌�#-35"
-                           value="鏌�#-35" />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
             <el-form-item label="鐗╂祦鏂瑰紡锛�"
                           prop="logisticsMethod">
               <el-select v-model="form.logisticsMethod"
@@ -457,8 +439,6 @@
               </el-select>
             </el-form-item>
           </el-col>
-        </el-row>
-        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="鎻愯揣鏂瑰紡锛�"
                           prop="pickupMethod">
@@ -468,6 +448,8 @@
               </el-radio-group>
             </el-form-item>
           </el-col>
+        </el-row>
+        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="閲囪喘娌瑰簱锛�"
                           prop="oilDepotId">
@@ -491,8 +473,6 @@
               </el-select>
             </el-form-item>
           </el-col>
-        </el-row>
-        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="瀹㈡埛鍚嶇О锛�"
                           prop="customerName">
@@ -502,6 +482,8 @@
                         :disabled="!['浠e偍', '閲囪喘鐩撮攢'].includes(form.purchaseCategory)" />
             </el-form-item>
           </el-col>
+        </el-row>
+        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="閲囪喘鏁伴噺锛堝惃锛夛細"
                           prop="purchaseQuantity">

--
Gitblit v1.9.3