| | |
| | | <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;"> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">新增台账</el-button> |
| | | <el-button type="success" |
| | | @click="openScanAddDialog">扫码新增</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | @expand-change="expandChange" |
| | | height="calc(100vh - 19em)" |
| | | :row-class-name="tableRowClassName"> |
| | | height="calc(100vh - 21.5em)"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" /> |
| | |
| | | width="60" /> |
| | | <el-table-column label="采购合同号" |
| | | prop="purchaseContractNumber" |
| | | width="200" |
| | | 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="订单状态" |
| | | width="100" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.isInvalid" |
| | | type="danger" |
| | | size="small">失效</el-tag> |
| | | <el-tag v-else |
| | | type="success" |
| | | size="small">正常</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="项目名称" |
| | | prop="projectName" |
| | | width="420" |
| | | width="320" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="审批状态" |
| | | prop="approvalStatus" |
| | | width="200" |
| | | width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tag size="small"> |
| | | <el-tag |
| | | :type="getApprovalStatusType(scope.row.approvalStatus)" |
| | | size="small"> |
| | | {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }} |
| | | </el-tag> |
| | | </template> |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="操作" |
| | | width="180" |
| | | width="120" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="openForm('edit', scope.row)">编辑</el-button> |
| | | <el-button link |
| | | type="success" |
| | | size="small" |
| | | @click="showQRCode(scope.row)">生成二维码</el-button> |
| | | @click="openForm('edit', scope.row)" |
| | | :disabled="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== 4">编辑</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | </div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | <FormDialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? '新增采购台账页面' : '编辑采购台账页面'" |
| | | width="70%" |
| | | @close="closeDia"> |
| | | :width="'70%'" |
| | | :operation-type="operationType" |
| | | @close="closeDia" |
| | | @confirm="submitForm" |
| | | @cancel="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="审批人:" |
| | | prop="approverId"> |
| | | <el-select v-model="form.approverId" |
| | | placeholder="请选择审批人" |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="录入人:" |
| | | prop="recorderId" |
| | | v-show="false"> |
| | | prop="recorderId"> |
| | | <el-select v-model="form.recorderId" |
| | | placeholder="请选择" |
| | | clearable |
| | | disabled> |
| | | filterable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | |
| | | </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"> |
| | |
| | | @click="deleteProduct">删除</el-button> |
| | | </el-form-item> |
| | | <div class="select-button-group" |
| | | style="width: 220px; margin: 20px 0;" |
| | | 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" |
| | | style="width: 180px; border-right: none; border-radius: 4px 0 0 4px;" |
| | | placeholder="请选择" |
| | | @focus="getTemplateList" |
| | | style="width: 500px;" |
| | | placeholder="请选择模版或者输入新的模版名称后选择" |
| | | class="no-arrow-select"> |
| | | <el-option v-for="item in templateList" |
| | | :key="item.value" |
| | | :key="item.id || item.value" |
| | | :label="item.templateName" |
| | | :value="item.templateName"></el-option> |
| | | :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; border-radius: 0 4px 4px 0; margin-left: -1px;" |
| | | style="height: 32px;margin-left: 8px;" |
| | | @click="handleButtonClick" |
| | | :disabled="!templateName || templateName.trim() === '' || isTemplateNameDuplicate"> |
| | | :disabled="!templateName || templateName.trim() === '' || (!currentTemplateId && isTemplateNameDuplicate)"> |
| | | 保存 |
| | | </el-button> |
| | | </div> |
| | |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog v-model="productFormVisible" |
| | | </FormDialog> |
| | | <FormDialog v-model="productFormVisible" |
| | | :title="productOperationType === 'add' ? '新增产品' : '编辑产品'" |
| | | width="40%" |
| | | @close="closeProductDia"> |
| | | :width="'40%'" |
| | | :operation-type="productOperationType" |
| | | @close="closeProductDia" |
| | | @confirm="submitProduct" |
| | | @cancel="closeProductDia"> |
| | | <el-form :model="productForm" |
| | | label-width="140px" |
| | | label-position="top" |
| | |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitProduct">确认</el-button> |
| | | <el-button @click="closeProductDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 二维码显示对话框 --> |
| | | <el-dialog v-model="qrCodeDialogVisible" |
| | | title="采购合同号二维码" |
| | | width="400px" |
| | | center> |
| | | <div style="text-align: center;"> |
| | | <img :src="qrCodeUrl" |
| | | alt="二维码" |
| | | style="width:200px;height:200px;" /> |
| | | <div style="margin: 20px;"> |
| | | <el-button type="primary" |
| | | @click="downloadQRCode">下载二维码图片</el-button> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | <!-- 扫码新增对话框 --> |
| | | <el-dialog v-model="scanAddDialogVisible" |
| | | title="扫码新增采购台账" |
| | | width="70%" |
| | | @close="closeScanAddDialog"> |
| | | <el-form :model="scanAddForm" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="scanAddRules" |
| | | ref="scanAddFormRef"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="扫码内容:"> |
| | | <el-input v-model="scanAddForm.scanContent" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请扫描二维码或手动输入采购合同信息" |
| | | @input="parseScanContent" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="采购合同号:" |
| | | prop="purchaseContractNumber"> |
| | | <el-input v-model="scanAddForm.purchaseContractNumber" |
| | | placeholder="请输入" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="供应商名称:" |
| | | prop="supplierName"> |
| | | <el-input v-model="scanAddForm.supplierName" |
| | | placeholder="请输入" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项目名称:" |
| | | prop="projectName"> |
| | | <el-input v-model="scanAddForm.projectName" |
| | | placeholder="请输入" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="合同金额(元):" |
| | | prop="contractAmount"> |
| | | <el-input-number v-model="scanAddForm.contractAmount" |
| | | :precision="2" |
| | | :step="0.1" |
| | | clearable |
| | | style="width: 100%" |
| | | placeholder="请输入" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="付款方式:"> |
| | | <el-input v-model="scanAddForm.paymentMethod" |
| | | placeholder="请输入" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="录入人:"> |
| | | <el-input v-model="scanAddForm.recorderName" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注:"> |
| | | <el-input v-model="scanAddForm.remark" |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请输入备注信息" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitScanAdd">确认新增</el-button> |
| | | <el-button @click="closeScanAddDialog">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 扫码登记对话框 --> |
| | | <el-dialog v-model="scanDialogVisible" |
| | | title="扫码登记" |
| | | width="60%" |
| | | @close="closeScanDialog"> |
| | | <el-form :model="scanForm" |
| | | label-width="120px" |
| | | label-position="left" |
| | | :rules="scanRules" |
| | | ref="scanFormRef"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="采购合同号:"> |
| | | <el-input v-model="scanForm.purchaseContractNumber" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="供应商名称:"> |
| | | <el-input v-model="scanForm.supplierName" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项目名称:"> |
| | | <el-input v-model="scanForm.projectName" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="扫码时间:"> |
| | | <el-input v-model="scanForm.scanTime" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="扫码人:"> |
| | | <el-input v-model="scanForm.scannerName" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="扫码状态:"> |
| | | <el-tag :type="scanForm.scanStatus === '已扫码' ? 'success' : 'warning'"> |
| | | {{ scanForm.scanStatus }} |
| | | </el-tag> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="扫码备注:"> |
| | | <el-input v-model="scanForm.scanRemark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入扫码备注信息" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="扫码记录:"> |
| | | <el-table :data="scanRecords" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table-column label="序号" |
| | | type="index" |
| | | width="60" |
| | | align="center" /> |
| | | <el-table-column label="扫码时间" |
| | | prop="scanTime" |
| | | width="180" /> |
| | | <el-table-column label="扫码人" |
| | | prop="scannerName" |
| | | width="120" /> |
| | | <el-table-column label="扫码状态" |
| | | prop="scanStatus" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.scanStatus === '已扫码' ? 'success' : 'warning'"> |
| | | {{ scope.row.scanStatus }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="备注" |
| | | prop="scanRemark" /> |
| | | </el-table> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitScan">确认扫码</el-button> |
| | | <el-button @click="closeScanDialog">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <FileList ref="fileListRef" /> |
| | | </FormDialog> |
| | | <FileListDialog |
| | | ref="fileListRef" |
| | | v-model="fileListDialogVisible" |
| | | title="附件列表" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | getCurrentInstance, |
| | | nextTick, |
| | | } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { Search, Delete } from "@element-plus/icons-vue"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import FileList from "./fileList.vue"; |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue'; |
| | | import FileListDialog from '@/components/Dialog/FileListDialog.vue'; |
| | | import { |
| | | getSalesLedgerWithProducts, |
| | | addOrUpdateSalesLedgerProduct, |
| | |
| | | import { |
| | | addOrEditPurchase, |
| | | addPurchaseTemplate, |
| | | updatePurchaseTemplate, |
| | | createPurchaseNo, |
| | | delPurchase, |
| | | getSalesNo, |
| | |
| | | getPurchaseById, |
| | | getOptions, |
| | | getPurchaseTemplateList, |
| | | delPurchaseTemplate, |
| | | } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | import QRCode from "qrcode"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | // 二维码相关变量 |
| | | const qrCodeDialogVisible = ref(false); |
| | | const qrCodeUrl = ref(""); |
| | | // 审批人节点(仿销售台账发货审批人) |
| | | 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 = { |
| | | 0: "审批中", |
| | | 1: "审批通过", |
| | | 2: "审批失败", |
| | | 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 (matchedTemplate?.id) { |
| | | // 如果找到模板,加载模板数据 |
| | | form.value = { |
| | | ...form.value, |
| | | ...matchedTemplate, |
| | | }; |
| | | productData.value = matchedTemplate.productData || []; |
| | | // 生成新的采购合同号 |
| | | try { |
| | | const res = await createPurchaseNo(); |
| | | if (res?.data) { |
| | | form.value.purchaseContractNumber = res.data; |
| | | // 记录当前选中的模板ID,后续保存时进行更新操作 |
| | | currentTemplateId.value = matchedTemplate.id; |
| | | // 选中已有模板时,不应视为“模板名称重复导致不可保存” |
| | | isTemplateNameDuplicate.value = false; |
| | | // 如果找到模板,只赋值供应商、项目名称、付款方式和产品信息 |
| | | if (matchedTemplate.supplierId) { |
| | | form.value.supplierId = matchedTemplate.supplierId; |
| | | } |
| | | } catch (error) { |
| | | console.error("生成采购合同号失败:", error); |
| | | 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]; |
| | |
| | | return; |
| | | } |
| | | |
| | | // 检查模板名称是否重复 |
| | | // 如果是“新增模板”(没有选中已有模板),才需要做重名校验; |
| | | // 若是选中已有模板后修改,则允许使用原名称(视为更新) |
| | | if (!currentTemplateId.value) { |
| | | const isDuplicate = checkTemplateNameDuplicate(templateName.value); |
| | | if (isDuplicate) { |
| | | ElMessage({ |
| | |
| | | type: "warning", |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 检查供应商是否选择 |
| | |
| | | } |
| | | |
| | | // 检查是否有产品数据 |
| | | // if (!productData.value || productData.value.length === 0) { |
| | | // 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, |
| | | approverId: form.value.approverId, |
| | | projectName: form.value.projectName, |
| | | approveUserIds: approveUserIds, |
| | | templateName: templateName.value.trim(), |
| | | }; |
| | | console.log(params); |
| | | let res = await addPurchaseTemplate(params); |
| | | 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: "模板保存成功", |
| | | message: currentTemplateId.value ? "模板更新成功" : "模板保存成功", |
| | | type: "success", |
| | | }); |
| | | // 保存成功后重新获取模板列表 |
| | |
| | | templateName.value = ""; |
| | | filterInputValue.value = ""; |
| | | isTemplateNameDuplicate.value = false; |
| | | // 保存/更新完成后,重置当前模板ID |
| | | currentTemplateId.value = null; |
| | | } else { |
| | | ElMessage({ |
| | | message: res?.msg || "模板保存失败", |
| | |
| | | // tableData.value = res.data.records; |
| | | tableData.value = res.data.records.map(record => ({ |
| | | ...record, |
| | | isInvalid: record.isWhite === 1, |
| | | })); |
| | | // 初始化子数据数组 |
| | | tableData.value.forEach(item => { |
| | |
| | | }; |
| | | // 打开弹框 |
| | | 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 = {}; |
| | |
| | | templateName.value = ""; |
| | | filterInputValue.value = ""; |
| | | isTemplateNameDuplicate.value = false; |
| | | // 重置审批人节点(默认一个空节点) |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | nextApproverId = 2; |
| | | try { |
| | | // 并行加载基础数据 |
| | | const [userRes, salesRes, supplierRes] = await Promise.all([ |
| | |
| | | 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("加载数据失败"); |
| | |
| | | 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) { |
| | | form.value.productData = proxy.HaveJson(productData.value); |
| | | // 新增时,需要从每个产品对象中删除 id 字段 |
| | | let processedProductData = productData.value; |
| | | if (operationType.value === "add") { |
| | | processedProductData = productData.value.map(product => { |
| | | const { id, ...rest } = product; |
| | | return rest; |
| | | }); |
| | | } |
| | | form.value.productData = proxy.HaveJson(processedProductData); |
| | | } else { |
| | | proxy.$modal.msgWarning("请添加产品信息"); |
| | | return; |
| | |
| | | } |
| | | form.value.tempFileIds = tempFileIds; |
| | | form.value.type = 2; |
| | | form.value.approveUserIds = approveUserIds; |
| | | |
| | | // 如果salesLedgerId为空,则不传递salesContractNo |
| | | if (!form.value.salesLedgerId) { |
| | | form.value.salesContractNo = ""; |
| | | } |
| | | |
| | | addOrEditPurchase(form.value).then(res => { |
| | | // 新增时不传递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 = (type, row, index) => { |
| | | const openProductForm = async (type, row, index) => { |
| | | productOperationType.value = type; |
| | | productOperationIndex.value = index; |
| | | productForm.value = {}; |
| | | proxy.resetForm("productFormRef"); |
| | | if (type === "edit") { |
| | | productForm.value = { ...row }; |
| | | } |
| | | productFormVisible.value = true; |
| | | getProductOptions(); |
| | | |
| | | // 先获取产品选项,确保数据加载完成 |
| | | await getProductOptions(); |
| | | |
| | | // 等待 DOM 更新 |
| | | await nextTick(); |
| | | |
| | | if (type === "edit") { |
| | | // 复制行数据 |
| | | productForm.value = { ...row }; |
| | | |
| | | // 如果是从模板加载的数据,可能没有 productId 和 productModelId |
| | | // 需要根据 productCategory 和 specificationModel 来查找对应的 ID |
| | | if (!productForm.value.productId && productForm.value.productCategory) { |
| | | // 根据 productCategory 查找 productId |
| | | const findProductIdByCategory = (nodes, categoryName) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].label === categoryName) { |
| | | return nodes[i].value; |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const found = findProductIdByCategory(nodes[i].children, categoryName); |
| | | if (found) return found; |
| | | } |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory); |
| | | if (productId) { |
| | | productForm.value.productId = productId; |
| | | // 获取型号列表并等待完成 |
| | | const modelRes = await modelList({ id: productId }); |
| | | modelOptions.value = modelRes; |
| | | |
| | | // 等待 DOM 更新 |
| | | await nextTick(); |
| | | |
| | | // 根据 specificationModel 查找 productModelId |
| | | if (productForm.value.specificationModel && modelOptions.value.length > 0) { |
| | | const modelItem = modelOptions.value.find( |
| | | item => item.model === productForm.value.specificationModel |
| | | ); |
| | | if (modelItem) { |
| | | productForm.value.productModelId = modelItem.id; |
| | | // 设置规格型号和单位 |
| | | getProductModel(modelItem.id); |
| | | } |
| | | } |
| | | } |
| | | } else if (productForm.value.productId) { |
| | | // 如果有 productId,正常加载型号列表 |
| | | await getModels(productForm.value.productId); |
| | | |
| | | // 等待 DOM 更新 |
| | | await nextTick(); |
| | | |
| | | if (productForm.value.productModelId) { |
| | | getProductModel(productForm.value.productModelId); |
| | | } |
| | | } |
| | | |
| | | // 最后再等待一次 DOM 更新,确保所有数据都已设置 |
| | | await nextTick(); |
| | | } |
| | | }; |
| | | const getProductOptions = () => { |
| | | productTreeList().then(res => { |
| | | return productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | | return res; |
| | | }); |
| | | }; |
| | | const getModels = value => { |
| | | if (value) { |
| | | productForm.value.productCategory = |
| | | findNodeById(productOptions.value, value) || ""; |
| | | modelList({ id: value }).then(res => { |
| | | return modelList({ id: value }).then(res => { |
| | | modelOptions.value = res; |
| | | return res; |
| | | }); |
| | | } else { |
| | | productForm.value.productCategory = ""; |
| | | modelOptions.value = []; |
| | | return Promise.resolve([]); |
| | | } |
| | | }; |
| | | const getProductModel = value => { |
| | |
| | | }; |
| | | |
| | | const fileListRef = ref(null); |
| | | const fileListDialogVisible = ref(false); |
| | | const downLoadFile = row => { |
| | | if (fileListRef.value) { |
| | | fileListRef.value.open(row.salesLedgerFiles); |
| | | }; |
| | | |
| | | // 显示二维码 |
| | | const showQRCode = async row => { |
| | | try { |
| | | // 构建二维码内容,只包含采购合同号(纯文本) |
| | | const qrContent = row.purchaseContractNumber || ""; |
| | | // 检查内容是否为空 |
| | | if (!qrContent || qrContent.trim() === "") { |
| | | proxy.$modal.msgWarning("该行没有采购合同号,无法生成二维码"); |
| | | return; |
| | | } |
| | | qrCodeUrl.value = await QRCode.toDataURL(qrContent, { |
| | | width: 200, |
| | | margin: 2, |
| | | color: { |
| | | dark: "#000000", |
| | | light: "#FFFFFF", |
| | | }, |
| | | }); |
| | | qrCodeDialogVisible.value = true; |
| | | } catch (error) { |
| | | console.error("生成二维码失败:", error); |
| | | proxy.$modal.msgError("生成二维码失败:" + error.message); |
| | | } |
| | | }; |
| | | |
| | | // 下载二维码 |
| | | const downloadQRCode = () => { |
| | | if (!qrCodeUrl.value) { |
| | | proxy.$modal.msgWarning("二维码未生成"); |
| | | return; |
| | | } |
| | | |
| | | const a = document.createElement("a"); |
| | | a.href = qrCodeUrl.value; |
| | | a.download = `采购合同号二维码_${new Date().getTime()}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | proxy.$modal.msgSuccess("下载成功"); |
| | | }; |
| | | |
| | | // 扫码新增对话框相关变量 |
| | | const scanAddDialogVisible = ref(false); |
| | | const scanAddForm = reactive({ |
| | | scanContent: "", |
| | | purchaseContractNumber: "", |
| | | supplierName: "", |
| | | projectName: "", |
| | | contractAmount: "", |
| | | paymentMethod: "", |
| | | recorderName: "", |
| | | scanRemark: "", |
| | | }); |
| | | const scanAddRules = { |
| | | purchaseContractNumber: [ |
| | | { required: true, message: "请输入采购合同号", trigger: "blur" }, |
| | | ], |
| | | supplierName: [ |
| | | { required: true, message: "请输入供应商名称", trigger: "blur" }, |
| | | ], |
| | | projectName: [{ required: true, message: "请输入项目名称", trigger: "blur" }], |
| | | }; |
| | | |
| | | // 扫码登记对话框相关变量 |
| | | const scanDialogVisible = ref(false); |
| | | const scanForm = reactive({ |
| | | purchaseContractNumber: "", |
| | | supplierName: "", |
| | | projectName: "", |
| | | scanTime: "", |
| | | scannerName: "", |
| | | scanStatus: "未扫码", |
| | | scanRemark: "", |
| | | }); |
| | | const scanRules = { |
| | | scanRemark: [{ required: true, message: "请输入扫码备注", trigger: "blur" }], |
| | | }; |
| | | const scanRecords = ref([]); |
| | | |
| | | // 打开扫码新增对话框 |
| | | const openScanAddDialog = () => { |
| | | scanAddForm.scanContent = ""; |
| | | scanAddForm.purchaseContractNumber = ""; |
| | | scanAddForm.supplierName = ""; |
| | | scanAddForm.projectName = ""; |
| | | scanAddForm.contractAmount = ""; |
| | | scanAddForm.paymentMethod = ""; |
| | | scanAddForm.recorderName = userStore.nickName; |
| | | scanAddForm.scanRemark = ""; |
| | | scanAddDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 解析扫码内容(模拟解析二维码数据) |
| | | const parseScanContent = content => { |
| | | if (!content) return; |
| | | |
| | | // 模拟解析二维码内容,这里可以根据实际需求调整解析逻辑 |
| | | // 假设扫码内容格式为:合同号|供应商|金额|付款方式 |
| | | const parts = content.split("|"); |
| | | if (parts.length >= 2) { |
| | | scanAddForm.purchaseContractNumber = parts[0] || ""; |
| | | scanAddForm.supplierName = parts[1] || ""; |
| | | scanAddForm.contractAmount = parts[2] || ""; |
| | | scanAddForm.paymentMethod = parts[3] || ""; |
| | | scanAddForm.projectName = parts[4] || ""; |
| | | // scanAddForm.contractAmount = parts[3] || ""; |
| | | // scanAddForm.paymentMethod = parts[4] || ""; |
| | | } |
| | | }; |
| | | |
| | | // 关闭扫码新增对话框 |
| | | const closeScanAddDialog = () => { |
| | | scanAddDialogVisible.value = false; |
| | | proxy.resetForm("scanAddFormRef"); |
| | | }; |
| | | |
| | | // 提交扫码新增 |
| | | const submitScanAdd = () => { |
| | | proxy.$refs["scanAddFormRef"].validate(valid => { |
| | | if (valid) { |
| | | // 构建新增数据 |
| | | const newData = { |
| | | purchaseContractNumber: scanAddForm.purchaseContractNumber, |
| | | supplierName: scanAddForm.supplierName, |
| | | projectName: scanAddForm.projectName, |
| | | contractAmount: scanAddForm.contractAmount, |
| | | paymentMethod: scanAddForm.paymentMethod, |
| | | recorderName: scanAddForm.recorderName, |
| | | entryDate: getCurrentDate(), |
| | | remark: scanAddForm.scanRemark, |
| | | type: 2, |
| | | }; |
| | | |
| | | // 模拟新增成功 |
| | | proxy.$modal.msgSuccess("扫码新增成功!"); |
| | | closeScanAddDialog(); |
| | | |
| | | // 可以选择是否刷新列表 |
| | | // getList(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 打开扫码登记对话框 |
| | | const openScanDialog = row => { |
| | | scanForm.purchaseContractNumber = row.purchaseContractNumber; |
| | | scanForm.supplierName = row.supplierName; |
| | | scanForm.projectName = row.projectName; |
| | | scanForm.scanTime = getCurrentDateTime(); |
| | | scanForm.scannerName = userStore.nickName; |
| | | scanForm.scanStatus = "未扫码"; |
| | | scanForm.scanRemark = ""; |
| | | scanRecords.value = []; |
| | | scanDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 关闭扫码登记对话框 |
| | | const closeScanDialog = () => { |
| | | scanDialogVisible.value = false; |
| | | proxy.resetForm("scanFormRef"); |
| | | }; |
| | | |
| | | // 提交扫码登记 |
| | | const submitScan = () => { |
| | | proxy.$refs["scanFormRef"].validate(valid => { |
| | | if (valid) { |
| | | // 添加扫码记录 |
| | | scanRecords.value.push({ |
| | | ...scanForm, |
| | | id: Date.now(), // 模拟ID |
| | | scanTime: getCurrentDateTime(), |
| | | }); |
| | | scanForm.scanStatus = "已扫码"; |
| | | scanForm.scanRemark = scanForm.scanRemark || "无"; |
| | | proxy.$modal.msgSuccess("扫码登记成功!"); |
| | | closeScanDialog(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 获取当前日期时间 |
| | | function getCurrentDateTime() { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); |
| | | const month = String(now.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(now.getDate()).padStart(2, "0"); |
| | | const hours = String(now.getHours()).padStart(2, "0"); |
| | | const minutes = String(now.getMinutes()).padStart(2, "0"); |
| | | const seconds = String(now.getSeconds()).padStart(2, "0"); |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | } |
| | | |
| | | // 添加行类名方法 |
| | | const tableRowClassName = ({ row }) => { |
| | | return row.isInvalid ? "invalid-row" : ""; |
| | | }; |
| | | |
| | | // 获取模板信息 |
| | |
| | | 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", |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | |
| | |
| | | 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> |