进销存-升级
1.供应商往来展示联调修改
2.指标统计页面展示联调修改
3.用户管理新增修改时字段修改
4.不需要多用户登录
5.采购报表展示修改联调
6.借款管理页面开发联调
已添加3个文件
已修改2个文件
559 ■■■■■ 文件已修改
src/api/financialManagement/loanManagement.js 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/loanManagement/Modal.vue 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/loanManagement/index.vue 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementReport/index.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/index.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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,
  });
};
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 ä»£è¡¨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>
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>
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
        }
      }
    }
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()