From ae2a253b23738b9b33daedbe50fc599d8d0eac52 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 15 一月 2026 18:00:47 +0800
Subject: [PATCH] 进销存-升级 1.供应商往来展示联调修改 2.指标统计页面展示联调修改 3.用户管理新增修改时字段修改 4.不需要多用户登录 5.采购报表展示修改联调 6.借款管理页面开发联调

---
 src/views/system/user/index.vue                             |   17 +
 src/views/procurementManagement/procurementReport/index.vue |    7 
 src/views/financialManagement/loanManagement/Modal.vue      |  222 ++++++++++++++++++++++
 src/views/financialManagement/loanManagement/index.vue      |  276 +++++++++++++++++++++++++++
 src/api/financialManagement/loanManagement.js               |   37 +++
 5 files changed, 548 insertions(+), 11 deletions(-)

diff --git a/src/api/financialManagement/loanManagement.js b/src/api/financialManagement/loanManagement.js
new file mode 100644
index 0000000..46ea749
--- /dev/null
+++ b/src/api/financialManagement/loanManagement.js
@@ -0,0 +1,37 @@
+import request from "@/utils/request";
+
+// 鏌ヨ鍒楄〃
+export const listPage = (params) => {
+  return request({
+    url: "/borrowInfo/listPage",
+    method: "get",
+    params,
+  });
+};
+
+// 鏂板
+export function add(data) {
+  return request({
+    url: "/borrowInfo/add",
+    method: "post",
+    data: data,
+  });
+}
+
+// 缂栬緫
+export function update(data) {
+  return request({
+    url: "/borrowInfo/update",
+    method: "post",
+    data: data,
+  });
+}
+
+// 鍒犻櫎
+export const delAccountLoan = (data) => {
+  return request({
+    url: "/borrowInfo/delete",
+    method: "delete",
+    data,
+  });
+};
diff --git a/src/views/financialManagement/loanManagement/Modal.vue b/src/views/financialManagement/loanManagement/Modal.vue
new file mode 100644
index 0000000..73b2cc3
--- /dev/null
+++ b/src/views/financialManagement/loanManagement/Modal.vue
@@ -0,0 +1,222 @@
+<template>
+  <FormDialog
+    v-model="dialogVisible"
+    :title="dialogTitle"
+    :operationType="operationType"
+    width="60%"
+    @confirm="sendForm"
+    @close="close"
+    @cancel="close"
+  >
+    <el-form
+      ref="formRef"
+      :model="form"
+      :rules="formRules"
+      label-width="120px"
+    >
+      <el-form-item label="鍊熸浜哄鍚�" prop="borrowerName">
+        <el-input v-model="form.borrowerName" placeholder="璇疯緭鍏ュ�熸浜哄鍚�" />
+      </el-form-item>
+      <el-form-item label="鍊熸閲戦锛堝厓锛�" prop="borrowAmount">
+        <el-input-number
+          :step="0.01"
+          :min="0"
+          :precision="2"
+          style="width: 100%"
+          v-model="form.borrowAmount"
+          placeholder="璇疯緭鍏ュ�熸閲戦"
+        />
+      </el-form-item>
+      <el-form-item label="鍊熸鍒╃巼锛�%锛�" prop="interestRate">
+        <el-input-number
+          :step="0.01"
+          :min="0"
+          :precision="2"
+          style="width: 100%"
+          v-model="form.interestRate"
+          placeholder="璇疯緭鍏ュ�熸鍒╃巼锛屽锛�5.85"
+        />
+      </el-form-item>
+      <el-form-item label="鍊熸鏃ユ湡" prop="borrowDate">
+        <el-date-picker
+          style="width: 100%"
+          v-model="form.borrowDate"
+          format="YYYY-MM-DD"
+          value-format="YYYY-MM-DD"
+          type="date"
+          placeholder="璇烽�夋嫨鍊熸鏃ユ湡"
+          clearable
+        />
+      </el-form-item>
+      <!-- 瀹為檯杩樻鏃ユ湡锛氫粎鈥滆繕娆锯�濇椂鍙~ -->
+      <el-form-item
+        v-if="operationType === 'repay'"
+        label="瀹為檯杩樻鏃ユ湡"
+        prop="repayDate"
+      >
+        <el-date-picker
+          style="width: 100%"
+          v-model="form.repayDate"
+          format="YYYY-MM-DD"
+          value-format="YYYY-MM-DD"
+          type="date"
+          placeholder="璇烽�夋嫨瀹為檯杩樻鏃ユ湡锛堣繕娆惧悗濉啓锛�"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item label="澶囨敞" prop="remark">
+        <el-input
+          v-model="form.remark"
+          type="textarea"
+          :rows="3"
+          placeholder="璇疯緭鍏ュ娉紙鍊熸璇存槑锛�"
+        />
+      </el-form-item>
+    </el-form>
+  </FormDialog>
+</template>
+
+<script setup>
+import { add, update } from "@/api/financialManagement/loanManagement";
+import useFormData from "@/hooks/useFormData";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { ElMessage } from "element-plus";
+import { ref } from "vue";
+
+defineOptions({
+  name: "鍊熸鏂板缂栬緫",
+});
+
+const emits = defineEmits(["success"]);
+
+const formRef = ref(null);
+const dialogVisible = ref(false);
+const operationType = ref("add"); // add | edit
+const id = ref(undefined);
+const submitting = ref(false);
+
+const dialogTitle = (type) => {
+  if (type === "edit") return "缂栬緫鍊熸";
+  if (type === "repay") return "杩樻";
+  return "鏂板鍊熸";
+};
+
+const formRules = {
+  borrowerName: [{ required: true, trigger: "blur", message: "璇疯緭鍏ュ�熸浜哄鍚�" }],
+  borrowAmount: [{ required: true, trigger: "blur", message: "璇疯緭鍏ュ�熸閲戦" }],
+  interestRate: [{ required: true, trigger: "blur", message: "璇疯緭鍏ュ�熸鍒╃巼" }],
+  borrowDate: [{ required: true, trigger: "change", message: "璇烽�夋嫨鍊熸鏃ユ湡" }],
+  repayDate: [
+    {
+      validator: (_rule, value, callback) => {
+        if (operationType.value === "repay" && !value) {
+          callback(new Error("璇烽�夋嫨瀹為檯杩樻鏃ユ湡"));
+          return;
+        }
+        callback();
+      },
+      trigger: "change",
+    },
+  ],
+};
+
+const { form, resetForm } = useFormData({
+  borrowerName: undefined, // 鍊熸浜哄鍚�
+  borrowAmount: undefined, // 鍊熸閲戦锛堝厓锛�
+  interestRate: undefined, // 鍊熸鍒╃巼锛堝锛�5.85 浠h〃5.85%锛�
+  borrowDate: undefined, // 鍊熸鏃ユ湡
+  repayDate: undefined, // 瀹為檯杩樻鏃ユ湡锛堣繕娆惧悗濉厖锛�
+  remark: undefined, // 澶囨敞锛堝�熸璇存槑锛�
+});
+
+const sendForm = () => {
+  if (submitting.value) return;
+  formRef.value?.validate(async (valid) => {
+    if (valid) {
+      submitting.value = true;
+      try {
+        const isRepay = operationType.value === "repay";
+        // 杩樻锛氫笉灞曠ず status锛屼絾鎻愪氦鏃跺己鍒朵紶 status=2锛岃蛋鏇存柊鎺ュ彛
+        const payload = isRepay
+          ? { id: id.value, ...form, status: 2 }
+          : id.value
+            ? { id: id.value, ...form }
+            : form;
+
+        const { code } = isRepay
+          ? await update(payload)
+          : id.value
+            ? await update(payload)
+            : await add(payload);
+        if (code == 200) {
+          emits("success");
+          ElMessage({ message: "鎿嶄綔鎴愬姛", type: "success" });
+          close();
+        }
+      } finally {
+        submitting.value = false;
+      }
+    }
+  });
+};
+
+const close = () => {
+  resetForm();
+  formRef.value?.clearValidate();
+  id.value = undefined;
+  dialogVisible.value = false;
+};
+
+// 缂栬緫锛氱洿鎺ョ敤鍒楄〃琛屾暟鎹洖濉紙閬垮厤渚濊禆璇︽儏鎺ュ彛锛�
+const loadForm = async (row) => {
+  const rowId = row?.id;
+  operationType.value = "edit";
+  id.value = rowId;
+  dialogVisible.value = true;
+  if (rowId) {
+    form.borrowerName = row.borrowerName;
+    form.borrowAmount = row.borrowAmount;
+    form.interestRate = row.interestRate;
+    form.borrowDate = row.borrowDate;
+    form.repayDate = row.repayDate;
+    form.remark = row.remark;
+  } else {
+    resetForm();
+    formRef.value?.clearValidate();
+  }
+};
+
+// 杩樻锛氭墦寮�寮圭獥锛屼粎濉啓瀹為檯杩樻鏃ユ湡锛屾彁浜ゆ椂寮哄埗 status=2
+const repay = async (row) => {
+  const rowId = row?.id;
+  operationType.value = "repay";
+  id.value = rowId;
+  dialogVisible.value = true;
+  if (rowId) {
+    // 涓轰簡璧� update 鎺ュ彛鏇寸ǔ濡ワ紝甯︿笂鍘熸湁鏁版嵁锛涘彧璁╃敤鎴烽�� repayDate
+    form.borrowerName = row.borrowerName;
+    form.borrowAmount = row.borrowAmount;
+    form.interestRate = row.interestRate;
+    form.borrowDate = row.borrowDate;
+    form.remark = row.remark;
+    form.repayDate = undefined;
+  } else {
+    resetForm();
+    formRef.value?.clearValidate();
+  }
+};
+
+const openModal = () => {
+  operationType.value = "add";
+  id.value = undefined;
+  resetForm();
+  formRef.value?.clearValidate();
+  dialogVisible.value = true;
+};
+
+defineExpose({
+  openModal,
+  loadForm,
+  repay,
+});
+</script>
diff --git a/src/views/financialManagement/loanManagement/index.vue b/src/views/financialManagement/loanManagement/index.vue
new file mode 100644
index 0000000..202313a
--- /dev/null
+++ b/src/views/financialManagement/loanManagement/index.vue
@@ -0,0 +1,276 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="鍊熸浜哄鍚�:">
+        <el-input
+          v-model="filters.borrowerName"
+          placeholder="璇疯緭鍏ュ�熸浜哄鍚�"
+          clearable
+          style="width: 200px;"
+        />
+      </el-form-item>
+      <el-form-item label="鍊熸鏃ユ湡:">
+        <el-date-picker
+          v-model="filters.borrowDate"
+          value-format="YYYY-MM-DD"
+          format="YYYY-MM-DD"
+          type="daterange"
+          range-separator="鑷�"
+          start-placeholder="寮�濮嬫棩鏈�"
+          end-placeholder="缁撴潫鏃ユ湡"
+          clearable
+          @change="changeDaterange"
+        />
+      </el-form-item>
+      <el-form-item label="鍊熸鐘舵��:">
+        <el-select
+          v-model="filters.status"
+          placeholder="璇烽�夋嫨"
+          clearable
+          style="width: 200px;"
+        >
+          <el-option label="寰呰繕娆�" :value="1" />
+          <el-option label="宸茶繕娆�" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div></div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus"> 鏂板 </el-button>
+          <el-button @click="handleOut" icon="download">瀵煎嚭</el-button>
+          <el-button
+            type="danger"
+            icon="Delete"
+            :disabled="multipleList.length <= 0"
+            @click="deleteRow(multipleList.map((item) => item.id))"
+          >
+            鎵归噺鍒犻櫎
+          </el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        isSelection
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @selection-change="handleSelectionChange"
+        @pagination="changePage"
+      >
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="edit(row)">
+            缂栬緫
+          </el-button>
+          <el-button
+            v-if="row.status == 1"
+            type="primary"
+            link
+            @click="repay(row)"
+          >
+            杩樻
+          </el-button>
+        </template>
+      </PIMTable>
+    </div>
+    <Modal ref="modalRef" @success="getTableData"></Modal>
+  </div>
+</template>
+
+<script setup>
+import { usePaginationApi } from "@/hooks/usePaginationApi";
+import { listPage, delAccountLoan } from "@/api/financialManagement/loanManagement";
+import { onMounted, getCurrentInstance, ref, nextTick } from "vue";
+import Modal from "./Modal.vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import dayjs from "dayjs";
+
+defineOptions({
+  name: "鍊熸绠$悊",
+});
+
+// 琛ㄦ牸澶氶�夋閫変腑椤�
+const multipleList = ref([]);
+const { proxy } = getCurrentInstance();
+const modalRef = ref();
+
+const {
+  filters,
+  columns,
+  dataList,
+  pagination,
+  getTableData,
+  resetFilters,
+  onCurrentChange,
+} = usePaginationApi(
+  listPage,
+  {
+    borrowerName: undefined,
+    borrowDate: undefined,
+    status: undefined,
+  },
+  [
+    {
+      label: "鍊熸浜哄鍚�",
+      prop: "borrowerName",
+      width: 150,
+    },
+    {
+      label: "鍊熸閲戦锛堝厓锛�",
+      prop: "borrowAmount",
+      width: 150,
+      formatData: (val) => {
+        return val ? `楼${parseFloat(val).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '楼0.00';
+      },
+    },
+    {
+      label: "鍊熸鍒╃巼锛�%锛�",
+      prop: "interestRate",
+      width: 130,
+      formatData: (val) => {
+        return val ? `${parseFloat(val).toFixed(2)}%` : '-';
+      },
+    },
+    {
+      label: "鍊熸鏃ユ湡",
+      prop: "borrowDate",
+      width: 120,
+    },
+    {
+      label: "瀹為檯杩樻鏃ユ湡",
+      prop: "repayDate",
+      width: 130,
+    },
+    {
+      label: "鍊熸鐘舵��",
+      prop: "status",
+      width: 100,
+      dataType: "tag",
+      formatData: (params) => {
+        if (params == 1) {
+          return "寰呰繕娆�";
+        } else if (params == 2) {
+          return "宸茶繕娆�";
+        }
+        return null;
+      },
+      formatType: (params) => {
+        if (params == 1) {
+          return "error";
+        } else if (params == 2) {
+          return "success";
+        }
+        return null;
+      },
+    },
+    {
+      fixed: "right",
+      label: "鎿嶄綔",
+      dataType: "slot",
+      slot: "operation",
+      align: "center",
+      width: "120px",
+    },
+  ],
+  null,
+  {
+    // 灏嗗墠绔�熸鏃ユ湡鑼冨洿杞崲涓哄悗绔渶瑕佺殑 entryDateStart / entryDateEnd锛屽苟涓斾笉浼� borrowDate
+    borrowDate: (val) => {
+      if (val && val.length === 2) {
+        return {
+          entryDateStart: dayjs(val[0]).format("YYYY-MM-DD"),
+          entryDateEnd: dayjs(val[1]).format("YYYY-MM-DD"),
+        };
+      }
+      return {};
+    },
+  }
+);
+
+// 澶氶�夊悗鍋氫粈涔�
+const handleSelectionChange = (selectionList) => {
+  multipleList.value = selectionList;
+};
+
+const add = () => {
+  modalRef.value.openModal();
+};
+
+const edit = (row) => {
+  modalRef.value.loadForm(row);
+};
+
+const repay = (row) => {
+  modalRef.value.repay(row);
+};
+
+const changePage = ({ page, limit }) => {
+  pagination.currentPage = page;
+  pagination.pageSize = limit;
+  onCurrentChange(page);
+};
+
+const deleteRow = (id) => {
+  ElMessageBox.confirm("姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ユ暟鎹�, 鏄惁缁х画?", "鎻愮ず", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(async () => {
+    const { code } = await delAccountLoan(id);
+    if (code == 200) {
+      ElMessage({
+        type: "success",
+        message: "鍒犻櫎鎴愬姛",
+      });
+      getTableData();
+    }
+  });
+};
+
+const changeDaterange = (value) => {
+  if (value) {
+    filters.borrowDate = value;
+  } else {
+    filters.borrowDate = null;
+  }
+  getTableData();
+};
+
+const handleOut = () => {
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+    .then(() => {
+      proxy.download(`/borrowInfo/export`, {}, "鍊熸鍙拌处.xlsx");
+    })
+    .catch(() => {
+      proxy.$modal.msg("宸插彇娑�");
+    });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.table_list {
+  margin-top: unset;
+}
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 10px;
+}
+</style>
diff --git a/src/views/procurementManagement/procurementReport/index.vue b/src/views/procurementManagement/procurementReport/index.vue
index 60a900d..8c8f3f6 100644
--- a/src/views/procurementManagement/procurementReport/index.vue
+++ b/src/views/procurementManagement/procurementReport/index.vue
@@ -95,8 +95,7 @@
 // 缁熻鏁版嵁
 const businessSummaryStats = ref({
   totalAmount: 0,
-  productTypes: 0,
-  supplierCount: 0
+  productTypes: 0
 })
 
 // 琛ㄦ牸鍒楅厤缃紙鏍规嵁鍚庣瀛楁瀹氫箟锛�
@@ -243,12 +242,10 @@
           return sum + (parseFloat(item.purchaseAmount) || 0)
         }, 0)
         businessSummaryStats.value.productTypes = new Set(businessSummaryData.value.map(item => item.productCategory)).size
-        businessSummaryStats.value.supplierCount = new Set(businessSummaryData.value.map(item => item.supplierName).filter(Boolean)).size
       } else {
         businessSummaryStats.value = {
           totalAmount: 0,
-          productTypes: 0,
-          supplierCount: 0
+          productTypes: 0
         }
       }
     }
diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue
index 0855f11..f28463b 100644
--- a/src/views/system/user/index.vue
+++ b/src/views/system/user/index.vue
@@ -6,7 +6,7 @@
 				<pane size="16">
 					<el-col style="padding: 10px">
 						<div class="head-container">
-							<el-input v-model="deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" clearable prefix-icon="Search" style="margin-bottom: 20px" />
+							<el-input v-model="deptNames" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" clearable prefix-icon="Search" style="margin-bottom: 20px" />
 						</div>
 						<div class="head-container">
 							<el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" />
@@ -61,7 +61,7 @@
 							<el-table-column label="鐢ㄦ埛缂栧彿" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
 							<el-table-column label="鐧诲綍璐﹀彿" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
 							<el-table-column label="鐢ㄦ埛鏄电О" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
-							<el-table-column label="閮ㄩ棬" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
+							<el-table-column label="閮ㄩ棬" align="center" key="deptNames" prop="deptNames" v-if="columns[3].visible" :show-overflow-tooltip="true" />
 							<el-table-column label="鎵嬫満鍙风爜" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
 							<el-table-column label="鐘舵��" align="center" key="status" v-if="columns[5].visible">
 								<template #default="scope">
@@ -235,7 +235,7 @@
 const total = ref(0)
 const title = ref("")
 const dateRange = ref([])
-const deptName = ref("")
+const deptNames = ref("")
 const deptOptions = ref(undefined)
 const enabledDeptOptions = ref(undefined)
 const initPassword = ref(undefined)
@@ -298,7 +298,7 @@
 }
 
 /** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
-watch(deptName, val => {
+watch(deptNames, val => {
 	proxy.$refs["deptTreeRef"].filter(val)
 })
 
@@ -519,14 +519,19 @@
 function submitForm() {
 	proxy.$refs["userRef"].validate(valid => {
 		if (valid) {
+			// 褰掑睘閮ㄩ棬铏界劧鏄崟閫夛紝浣嗗悗绔渶瑕佷紶鏁扮粍瀛楁 deptIds
+			const payload = {
+				...form.value,
+				deptIds: form.value.deptId ? [form.value.deptId] : []
+			}
 			if (form.value.userId != undefined) {
-				updateUser(form.value).then(response => {
+				updateUser(payload).then(response => {
 					proxy.$modal.msgSuccess("淇敼鎴愬姛")
 					open.value = false
 					getList()
 				})
 			} else {
-				addUser(form.value).then(response => {
+				addUser(payload).then(response => {
 					proxy.$modal.msgSuccess("鏂板鎴愬姛")
 					open.value = false
 					getList()

--
Gitblit v1.9.3