<template>
|
<div class="app-container">
|
<div class="search_form">
|
<div>
|
<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 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>
|
<el-button type="primary"
|
@click="handleQuery"> 搜索 </el-button>
|
</el-form-item>
|
</el-form>
|
</div>
|
</div>
|
<div class="table_list">
|
<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-column label="产品大类"
|
prop="productCategory" />
|
<el-table-column label="规格型号"
|
prop="specificationModel" />
|
<el-table-column label="单位"
|
prop="unit" />
|
<el-table-column label="数量"
|
prop="quantity" />
|
<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>
|
</template>
|
</el-table-column>
|
<el-table-column align="center"
|
label="序号"
|
type="index"
|
width="60" />
|
<el-table-column label="采购合同号"
|
prop="purchaseContractNumber"
|
width="160"
|
show-overflow-tooltip />
|
<el-table-column 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-tag
|
:type="getApprovalStatusType(scope.row.approvalStatus)"
|
size="small">
|
{{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }}
|
</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 fixed="right"
|
label="操作"
|
width="120"
|
align="center">
|
<template #default="scope">
|
<el-button link
|
type="primary"
|
size="small"
|
@click="openForm('edit', scope.row)"
|
:disabled="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== 4">编辑</el-button>
|
<el-button link
|
type="primary"
|
size="small"
|
@click="downLoadFile(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" />
|
</div>
|
<FormDialog v-model="dialogFormVisible"
|
:title="operationType === 'add' ? '新增采购台账页面' : '编辑采购台账页面'"
|
: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-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="销售合同号:"
|
prop="salesLedgerId">
|
<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>
|
</el-row>
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<el-form-item label="供应商名称:"
|
prop="supplierId">
|
<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" />
|
</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="付款方式">
|
<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 :gutter="30">
|
<el-col :span="24">
|
<el-form-item>
|
<template #label>
|
<div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
|
<span>审批人选择:</span>
|
<el-button type="primary" size="small" @click="addApproverNode" icon="Plus">新增节点</el-button>
|
</div>
|
</template>
|
<div class="approver-nodes-container">
|
<div
|
v-for="(node, index) in approverNodes"
|
:key="node.id"
|
class="approver-node-item"
|
>
|
<div class="approver-node-header">
|
<span class="approver-node-label">审批节点 {{ index + 1 }}</span>
|
<el-button
|
v-if="approverNodes.length > 1"
|
type="danger"
|
size="small"
|
text
|
@click="removeApproverNode(index)"
|
icon="Delete"
|
>删除</el-button>
|
</div>
|
<el-select
|
v-model="node.userId"
|
placeholder="请选择审批人"
|
filterable
|
style="width: 100%;"
|
>
|
<el-option
|
v-for="user in userList"
|
:key="user.userId"
|
:label="user.nickName"
|
:value="user.userId"
|
/>
|
</el-select>
|
</div>
|
</div>
|
</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-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-column align="center"
|
type="selection"
|
width="55" />
|
<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 label="是否质检"
|
prop="isChecked"
|
width="150">
|
<template #default="scope">
|
<el-tag :type="scope.row.isChecked ? 'success' : 'info'">
|
{{ scope.row.isChecked ? '是' : '否' }}
|
</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"
|
size="small"
|
@click="openProductForm('edit', scope.row, scope.$index)">编辑</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>
|
</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,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
|
</div>
|
</template>
|
</el-upload>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
</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 #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-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>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<el-form-item label="单位:"
|
prop="unit">
|
<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-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"
|
: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"
|
: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"
|
: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-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-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>
|
</FormDialog>
|
<FileListDialog
|
ref="fileListRef"
|
v-model="fileListDialogVisible"
|
title="附件列表"
|
/>
|
</div>
|
</template>
|
|
<script setup>
|
import { getToken } from "@/utils/auth";
|
import pagination from "@/components/PIMTable/Pagination.vue";
|
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 FormDialog from '@/components/Dialog/FormDialog.vue';
|
import FileListDialog from '@/components/Dialog/FileListDialog.vue';
|
import {
|
getSalesLedgerWithProducts,
|
addOrUpdateSalesLedgerProduct,
|
delProduct,
|
delLedgerFile,
|
getProductInfoByContractNo,
|
} from "@/api/salesManagement/salesLedger.js";
|
import {
|
addOrEditPurchase,
|
addPurchaseTemplate,
|
updatePurchaseTemplate,
|
createPurchaseNo,
|
delPurchase,
|
getSalesNo,
|
purchaseListPage,
|
productList,
|
getPurchaseById,
|
getOptions,
|
getPurchaseTemplateList,
|
delPurchaseTemplate,
|
} from "@/api/procurementManagement/procurementLedger.js";
|
import useFormData from "@/hooks/useFormData.js";
|
|
const { proxy } = getCurrentInstance();
|
const tableData = ref([]);
|
const productData = ref([]);
|
const selectedRows = ref([]);
|
const productSelectedRows = ref([]);
|
const modelOptions = ref([]);
|
const userList = ref([]);
|
const productOptions = ref([]);
|
const salesContractList = ref([]);
|
const supplierList = ref([]);
|
const tableLoading = ref(false);
|
const page = reactive({
|
current: 1,
|
size: 100,
|
});
|
const total = ref(0);
|
const fileList = ref([]);
|
import useUserStore from "@/store/modules/user";
|
import { modelList, productTreeList } from "@/api/basicData/product.js";
|
import dayjs from "dayjs";
|
|
const userStore = useUserStore();
|
|
// 审批人节点(仿销售台账发货审批人)
|
const approverNodes = ref([{ id: 1, userId: null }]);
|
let nextApproverId = 2;
|
const addApproverNode = () => {
|
approverNodes.value.push({ id: nextApproverId++, userId: null });
|
};
|
const removeApproverNode = (index) => {
|
approverNodes.value.splice(index, 1);
|
};
|
|
// 订单审批状态显示文本
|
const approvalStatusText = {
|
1: "待审核",
|
2: "审批中",
|
3: "审批通过",
|
4: "审批失败",
|
};
|
|
// 获取审批状态标签类型
|
const getApprovalStatusType = (status) => {
|
const typeMap = {
|
1: "info", // 待审核 - 灰色
|
2: "warning", // 审批中 - 橙色
|
3: "success", // 审批通过 - 绿色
|
4: "danger", // 审批失败 - 红色
|
};
|
return typeMap[status] || "";
|
};
|
|
const templateName = ref("");
|
const filterInputValue = ref("");
|
const templateList = ref([]);
|
const isTemplateNameDuplicate = ref(false); // 标记模板名称是否重复
|
// 当前选中的模板ID(用于区分新增模板还是更新模板)
|
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;
|
};
|
|
// 防抖定时器
|
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) {
|
// 记录当前选中的模板ID,后续保存时进行更新操作
|
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: {
|
supplierName: "", // 供应商名称
|
purchaseContractNumber: "", // 采购合同编号
|
salesContractNo: "", // 销售合同编号
|
projectName: "", // 项目名称
|
entryDate: null, // 录入日期
|
entryDateStart: undefined,
|
entryDateEnd: undefined,
|
},
|
form: {
|
purchaseContractNumber: "",
|
salesLedgerId: "",
|
projectName: "",
|
recorderId: "",
|
entryDate: "",
|
productData: [],
|
supplierName: "",
|
supplierId: "",
|
paymentMethod: "",
|
executionDate: "",
|
isChecked: true,
|
},
|
rules: {
|
purchaseContractNumber: [
|
{ required: true, message: "请输入", trigger: "blur" },
|
],
|
approverId: [
|
{ required: true, message: "请选择审批人", trigger: "change" },
|
],
|
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 { 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("");
|
const productOperationIndex = ref("");
|
const currentId = ref("");
|
const productFormData = reactive({
|
productForm: {
|
productId: "",
|
productCategory: "",
|
productModelId: "",
|
specificationModel: "",
|
unit: "",
|
quantity: "",
|
taxInclusiveUnitPrice: "",
|
taxRate: "",
|
taxInclusiveTotalPrice: "",
|
taxExclusiveTotalPrice: "",
|
invoiceType: "",
|
warnNum: "",
|
isChecked: true,
|
},
|
productRules: {
|
productId: [{ required: true, message: "请选择", trigger: "change" }],
|
productModelId: [{ required: true, message: "请选择", trigger: "change" }],
|
unit: [{ required: true, message: "请输入", trigger: "blur" }],
|
quantity: [{ required: true, message: "请输入", trigger: "blur" }],
|
taxInclusiveUnitPrice: [
|
{ required: true, message: "请输入", trigger: "blur" },
|
],
|
taxRate: [{ required: true, message: "请选择", trigger: "change" }],
|
warnNum: [{ required: true, message: "请选择", trigger: "change" }],
|
taxInclusiveTotalPrice: [
|
{ required: true, message: "请输入", trigger: "blur" },
|
],
|
taxExclusiveTotalPrice: [
|
{ required: true, message: "请输入", trigger: "blur" },
|
],
|
invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
|
isChecked: [{ required: true, message: "请选择", trigger: "change" }],
|
},
|
});
|
const { productForm, productRules } = toRefs(productFormData);
|
const upload = reactive({
|
// 上传的地址
|
url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
|
// 设置上传的请求头部
|
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);
|
};
|
// 查询列表
|
/** 搜索按钮操作 */
|
const handleQuery = () => {
|
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 {
|
// 获取审批人ID字符串
|
const approveUserIds = approverNodes.value
|
.filter(node => node.userId)
|
.map(node => node.userId)
|
.join(",");
|
|
let params = {
|
productData: proxy.HaveJson(productData.value),
|
supplierId: form.value.supplierId,
|
paymentMethod: form.value.paymentMethod,
|
recorderId: form.value.recorderId,
|
projectName: form.value.projectName,
|
approveUserIds: approveUserIds,
|
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 => {
|
return proxy.summarizeTable(
|
param,
|
[
|
"taxInclusiveUnitPrice",
|
"taxInclusiveTotalPrice",
|
"taxExclusiveTotalPrice",
|
"ticketsNum",
|
"ticketsAmount",
|
"futureTickets",
|
"futureTicketsAmount",
|
],
|
{
|
ticketsNum: { noDecimal: true }, // 不保留小数
|
futureTickets: { noDecimal: true }, // 不保留小数
|
}
|
);
|
};
|
const paginationChange = obj => {
|
page.current = obj.page;
|
page.size = obj.limit;
|
getList();
|
};
|
const getList = () => {
|
tableLoading.value = true;
|
const { entryDate, ...rest } = searchForm;
|
purchaseListPage({ ...rest, ...page })
|
.then(res => {
|
tableLoading.value = false;
|
// tableData.value = res.data.records;
|
tableData.value = res.data.records.map(record => ({
|
...record,
|
}));
|
// 初始化子数据数组
|
tableData.value.forEach(item => {
|
item.children = [];
|
});
|
total.value = res.data.total;
|
expandedRowKeys.value = [];
|
})
|
.catch(() => {
|
tableLoading.value = false;
|
});
|
};
|
// 表格选择数据
|
const handleSelectionChange = selection => {
|
selectedRows.value = selection;
|
};
|
const productSelected = selectedRows => {
|
productSelectedRows.value = selectedRows;
|
};
|
const expandedRowKeys = ref([]);
|
// 展开行
|
const expandChange = async (row, expandedRows) => {
|
if (expandedRows.length > 0) {
|
expandedRowKeys.value = [];
|
try {
|
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.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 => {
|
return proxy.summarizeTable(param, ["contractAmount"]);
|
};
|
// 子表合计方法
|
const summarizeProTable = param => {
|
return proxy.summarizeTable(param, [
|
"taxInclusiveUnitPrice",
|
"taxInclusiveTotalPrice",
|
"taxExclusiveTotalPrice",
|
]);
|
};
|
// 打开弹框
|
const openForm = async (type, row) => {
|
// 编辑时检查审核状态,只有待审核(1)和审批失败(4)才能编辑
|
if (type === "edit" && row) {
|
if (row.approvalStatus !== 1 && row.approvalStatus !== 4) {
|
proxy.$modal.msgWarning("只有待审核和审批失败状态的记录才能编辑");
|
return;
|
}
|
}
|
|
await getTemplateList();
|
operationType.value = type;
|
form.value = {};
|
productData.value = [];
|
fileList.value = [];
|
templateName.value = "";
|
filterInputValue.value = "";
|
isTemplateNameDuplicate.value = false;
|
// 重置审批人节点(默认一个空节点)
|
approverNodes.value = [{ id: 1, userId: null }];
|
nextApproverId = 2;
|
try {
|
// 并行加载基础数据
|
const [userRes, salesRes, supplierRes] = await Promise.all([
|
userListNoPage(),
|
getSalesNo(),
|
getOptions(),
|
]);
|
|
userList.value = userRes.data || [];
|
salesContractList.value = salesRes || [];
|
// 供应商过滤出isWhite=0 的数据
|
supplierList.value = (supplierRes.data || []).filter(
|
item => item.isWhite === 0
|
);
|
|
// 设置默认值
|
form.value.recorderId = userStore.id;
|
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 };
|
productData.value = purchaseRes.productData || [];
|
fileList.value = purchaseRes.salesLedgerFiles || [];
|
// 如果编辑时有审批人,解析审批人ID字符串并设置到节点中
|
if (purchaseRes.approveUserIds) {
|
const approverIds = purchaseRes.approveUserIds.split(",");
|
approverNodes.value = approverIds.map((id, index) => ({
|
id: index + 1,
|
userId: Number(id)
|
}));
|
nextApproverId = approverIds.length + 1;
|
}
|
} catch (error) {
|
console.error("加载采购台账数据失败:", error);
|
proxy.$modal.msgError("加载数据失败");
|
return;
|
}
|
}
|
|
if (form.value.salesLedgerId == -1) {
|
form.value.salesLedgerId = null;
|
}
|
console.log(form.value, "form.value===========");
|
dialogFormVisible.value = true;
|
} catch (error) {
|
console.error("打开表单失败:", error);
|
proxy.$modal.msgError("加载基础数据失败");
|
}
|
};
|
// 上传前校检
|
function handleBeforeUpload(file) {
|
// 校检文件大小
|
if (file.size > 1024 * 1024 * 10) {
|
proxy.$modal.msgError("上传文件大小不能超过10MB!");
|
return false;
|
}
|
proxy.$modal.loading("正在上传文件,请稍候...");
|
return true;
|
}
|
// 上传失败
|
function handleUploadError(err) {
|
proxy.$modal.msgError("上传文件失败");
|
proxy.$modal.closeLoading();
|
}
|
// 上传成功回调
|
function handleUploadSuccess(res, file, uploadFiles) {
|
proxy.$modal.closeLoading();
|
if (res.code === 200) {
|
file.tempId = res.data.tempId;
|
proxy.$modal.msgSuccess("上传成功");
|
} else {
|
proxy.$modal.msgError(res.msg);
|
proxy.$refs.fileUpload.handleRemove(file);
|
}
|
}
|
// 移除文件
|
async function handleRemove(file) {
|
if (!file?.id) {
|
return;
|
}
|
console.log("handleRemove", file.id);
|
if (file.size > 1024 * 1024 * 10) {
|
// 仅前端清理,不调用删除接口和提示
|
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 => {
|
if (valid) {
|
// 审批人必填校验(所有节点都要选人)
|
const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
|
if (hasEmptyApprover) {
|
proxy.$modal.msgError("请为所有审批节点选择审批人!");
|
return;
|
}
|
const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
|
|
if (productData.value.length > 0) {
|
// 新增时,需要从每个产品对象中删除 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.type = 2;
|
form.value.approveUserIds = approveUserIds;
|
|
// 如果salesLedgerId为空,则不传递salesContractNo
|
if (!form.value.salesLedgerId) {
|
form.value.salesContractNo = "";
|
}
|
|
// 新增时不传递id
|
const submitData = { ...form.value };
|
if (operationType.value === "add") {
|
delete submitData.id;
|
}
|
|
addOrEditPurchase(submitData).then(res => {
|
proxy.$modal.msgSuccess("提交成功");
|
closeDia();
|
getList();
|
});
|
}
|
});
|
};
|
// 关闭弹框
|
const closeDia = () => {
|
proxy.resetForm("formRef");
|
// 重置审批人节点(默认一个空节点)
|
approverNodes.value = [{ id: 1, userId: null }];
|
nextApproverId = 2;
|
dialogFormVisible.value = false;
|
};
|
// 打开产品弹框
|
const openProductForm = async (type, row, index) => {
|
productOperationType.value = type;
|
productOperationIndex.value = index;
|
productForm.value = {};
|
proxy.resetForm("productFormRef");
|
productFormVisible.value = true;
|
|
// 先获取产品选项,确保数据加载完成
|
await getProductOptions();
|
|
// 等待 DOM 更新
|
await nextTick();
|
|
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 = () => {
|
return productTreeList().then(res => {
|
productOptions.value = convertIdToValue(res);
|
return 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);
|
if (index !== -1) {
|
productForm.value.specificationModel = modelOptions.value[index].model;
|
productForm.value.unit = modelOptions.value[index].unit;
|
} else {
|
productForm.value.specificationModel = null;
|
productForm.value.unit = null;
|
}
|
};
|
const findNodeById = (nodes, productId) => {
|
for (let i = 0; i < nodes.length; i++) {
|
if (nodes[i].value === productId) {
|
return nodes[i].label; // 找到节点,返回该节点的label
|
}
|
if (nodes[i].children && nodes[i].children.length > 0) {
|
const foundNode = findNodeById(nodes[i].children, productId);
|
if (foundNode) {
|
return foundNode; // 在子节点中找到,直接返回(已经是label字符串)
|
}
|
}
|
}
|
return null; // 没有找到节点,返回null
|
};
|
function convertIdToValue(data) {
|
return data.map(item => {
|
const { id, children, ...rest } = item;
|
const newItem = {
|
...rest,
|
value: id, // 将 id 改为 value
|
};
|
if (children && children.length > 0) {
|
newItem.children = convertIdToValue(children);
|
}
|
|
return newItem;
|
});
|
}
|
// 提交产品表单
|
const submitProduct = () => {
|
proxy.$refs["productFormRef"].validate(valid => {
|
if (valid) {
|
if (operationType.value === "edit") {
|
submitProductEdit();
|
} else {
|
if (productOperationType.value === "add") {
|
productData.value.push({ ...productForm.value });
|
console.log("productData.value---", productData.value);
|
} else {
|
productData.value[productOperationIndex.value] = {
|
...productForm.value,
|
};
|
}
|
closeProductDia();
|
}
|
}
|
});
|
};
|
const submitProductEdit = () => {
|
productForm.value.salesLedgerId = currentId.value;
|
productForm.value.type = 2;
|
addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
|
proxy.$modal.msgSuccess("提交成功");
|
closeProductDia();
|
getPurchaseById({ id: currentId.value, type: 2 }).then(res => {
|
productData.value = res.productData;
|
});
|
});
|
};
|
// 删除产品
|
const deleteProduct = () => {
|
if (productSelectedRows.value.length === 0) {
|
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);
|
}
|
});
|
} else {
|
let ids = [];
|
if (productSelectedRows.value.length > 0) {
|
ids = productSelectedRows.value.map(item => item.id);
|
}
|
ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
delProduct(ids).then(res => {
|
proxy.$modal.msgSuccess("删除成功");
|
closeProductDia();
|
getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
|
res => {
|
productData.value = res.productData;
|
}
|
);
|
});
|
})
|
.catch(() => {
|
proxy.$modal.msg("已取消");
|
});
|
}
|
};
|
// 关闭产品弹框
|
const closeProductDia = () => {
|
proxy.resetForm("productFormRef");
|
productFormVisible.value = false;
|
};
|
// 导出
|
const handleOut = () => {
|
ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
proxy.download("/purchase/ledger/export", {}, "采购台账.xlsx");
|
})
|
.catch(() => {
|
proxy.$modal.msg("已取消");
|
});
|
};
|
// 删除
|
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);
|
} else {
|
proxy.$modal.msgWarning("请选择数据");
|
return;
|
}
|
ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
delPurchase(ids).then(res => {
|
proxy.$modal.msgSuccess("删除成功");
|
getList();
|
});
|
})
|
.catch(() => {
|
proxy.$modal.msg("已取消");
|
});
|
};
|
// 获取当前日期并格式化为 YYYY-MM-DD
|
function getCurrentDate() {
|
const today = new Date();
|
const year = today.getFullYear();
|
const month = String(today.getMonth() + 1).padStart(2, "0"); // 月份从0开始
|
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.taxInclusiveUnitPrice) {
|
return;
|
}
|
if (!productForm.value.quantity) {
|
return;
|
}
|
// 含税总价计算
|
productForm.value.taxInclusiveTotalPrice =
|
proxy.calculateTaxIncludeTotalPrice(
|
productForm.value.taxInclusiveUnitPrice,
|
productForm.value.quantity
|
);
|
if (productForm.value.taxRate) {
|
// 不含税总价计算
|
productForm.value.taxExclusiveTotalPrice =
|
proxy.calculateTaxExclusiveTotalPrice(
|
productForm.value.taxInclusiveTotalPrice,
|
productForm.value.taxRate
|
);
|
}
|
};
|
const reverseMathNum = field => {
|
if (!productForm.value.taxRate) {
|
proxy.$modal.msgWarning("请先选择税率");
|
return;
|
}
|
const taxRate = Number(productForm.value.taxRate);
|
if (!taxRate) return;
|
|
// 确保输入值不为负数
|
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);
|
// 确保结果不为负数
|
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);
|
// 确保结果不为负数
|
if (Number(productForm.value.quantity) < 0) {
|
productForm.value.quantity = "0";
|
}
|
}
|
// 反算不含税总价
|
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);
|
// 确保结果不为负数
|
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);
|
// 确保结果不为负数
|
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);
|
// 确保结果不为负数
|
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);
|
if (index > -1) {
|
await querygProductInfoByContractNo();
|
}
|
};
|
|
const querygProductInfoByContractNo = async () => {
|
const { code, data } = await getProductInfoByContractNo({
|
contractNo: form.value.salesLedgerId,
|
});
|
if (code == 200) {
|
productData.value = data;
|
}
|
};
|
|
const fileListRef = ref(null);
|
const fileListDialogVisible = ref(false);
|
const downLoadFile = row => {
|
if (fileListRef.value) {
|
fileListRef.value.open(row.salesLedgerFiles);
|
}
|
};
|
|
// 获取模板信息
|
const getTemplateList = async () => {
|
let res = await getPurchaseTemplateList();
|
if (res && res.code === 200 && Array.isArray(res.data)) {
|
templateList.value = res.data;
|
}
|
};
|
|
// 删除模板
|
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">
|
.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; /* 隐藏默认下拉箭头 */
|
}
|
.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>
|