| | |
| | | }) |
| | | } |
| | | |
| | | // 下载出库检验报告 |
| | | export function downloadOutReport(data) { |
| | | return request({ |
| | | url: '/quality/qualityInspect/downOutReport', |
| | | method: 'post', |
| | | data: data, |
| | | responseType: "blob", |
| | | }) |
| | | } |
| | |
| | | modelRules: { |
| | | model: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | unit: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | validityPeriod: [ |
| | | { required: true, type: "number", message: "请输入有效期", trigger: "change" }, |
| | | ], |
| | | }, |
| | | }); |
| | | const { form, rules, modelForm, modelRules } = toRefs(data); |
| | |
| | | </el-form-item> |
| | | |
| | | <el-form-item |
| | | v-if="type === 'qualified'" |
| | | label="生产日期" |
| | | prop="productionDate" |
| | | :rules="[{ required: true, message: '请选择生产日期', trigger: 'change' }]" |
| | | > |
| | | <el-date-picker |
| | | v-model="formState.productionDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="请选择生产日期" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item |
| | | label="供应商" |
| | | prop="customer" |
| | | :rules="[{ required: true, message: '请选择供应商', trigger: 'change' }]" |
| | |
| | | productModelName: "", |
| | | unit: "", |
| | | batchNo: "", |
| | | productionDate: "", |
| | | customer: "", |
| | | qualitity: 0, |
| | | warnNum: 0, |
| | |
| | | productModelName: "", |
| | | unit: "", |
| | | batchNo: "", |
| | | productionDate: "", |
| | | customer: "", |
| | | qualitity: 0, |
| | | warnNum: 0, |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title ml10">产品大类:</span> |
| | | <el-input v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请输入" |
| | | clearable/> |
| | | <span class="search_title ml10" style="margin-left: 20px">规格型号:</span> |
| | | <el-input v-model="searchForm.model" |
| | | style="width: 240px" |
| | | placeholder="请输入" |
| | | clearable/> |
| | | <span class="search_title ml10" style="margin-left: 20px">UID码:</span> |
| | | <el-input v-model="searchForm.uidNo" |
| | | style="width: 200px" |
| | | placeholder="请输入" |
| | | clearable/> |
| | | <span class="search_title ml10" style="margin-left: 20px">批次号:</span> |
| | | <el-input v-model="searchForm.batchNo" |
| | | style="width: 200px" |
| | | placeholder="请输入" |
| | | clearable/> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button> |
| | | <div class="search_form search_form--wrap"> |
| | | <div class="search-field"> |
| | | <span class="search_title">产品类型:</span> |
| | | <el-radio-group v-model="productScope" class="qualified-product-scope" @change="onProductScopeChange"> |
| | | <el-radio-button label="成品">成品</el-radio-button> |
| | | <el-radio-button label="其他产品">其他产品</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="isShowNewModal = true">新增库存</el-button> |
| | | <div class="search-field"> |
| | | <span class="search_title">产品名称:</span> |
| | | <el-input |
| | | v-model="searchForm.productName" |
| | | class="search-input" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </div> |
| | | <div class="search-field"> |
| | | <span class="search_title">规格型号:</span> |
| | | <el-input |
| | | v-model="searchForm.model" |
| | | class="search-input" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </div> |
| | | <div class="search-field"> |
| | | <span class="search_title">UID码:</span> |
| | | <el-input |
| | | v-model="searchForm.uidNo" |
| | | class="search-input" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </div> |
| | | <div class="search-field"> |
| | | <span class="search_title">批次号:</span> |
| | | <el-input |
| | | v-model="searchForm.batchNo" |
| | | class="search-input" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </div> |
| | | <div class="search-field search-field--actions"> |
| | | <el-button type="primary" @click="handleQuery">搜索</el-button> |
| | | <el-button type="primary" @click="isShowNewModal = true">新增库存</el-button> |
| | | <el-button type="info" plain icon="Upload" @click="isShowImportModal = true"> |
| | | 导入库存 |
| | | </el-button> |
| | |
| | | <div class="table_list"> |
| | | <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" :row-key="row => row.id" style="width: 100%" |
| | | :row-class-name="tableRowClassName" height="calc(100vh - 18.5em)"> |
| | | :row-class-name="tableRowClassName" height="calc(100vh - 26.5em)"> |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="序号" type="index" width="60" /> |
| | | <el-table-column label="产品大类" prop="productName" show-overflow-tooltip /> |
| | | <el-table-column label="规格型号" prop="model" show-overflow-tooltip /> |
| | | <el-table-column label="UID码" prop="uidNo" show-overflow-tooltip /> |
| | | <el-table-column label="批号" prop="batchNo" show-overflow-tooltip /> |
| | | <el-table-column label="生产日期" prop="productionDate" show-overflow-tooltip /> |
| | | <el-table-column label="供应商" prop="customer" show-overflow-tooltip /> |
| | | <el-table-column label="单位" prop="unit" show-overflow-tooltip /> |
| | | <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip /> |
| | |
| | | <el-table-column label="库存预警数量" prop="warnNum" show-overflow-tooltip /> |
| | | <el-table-column label="备注" prop="remark" show-overflow-tooltip /> |
| | | <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="操作" min-width="60" align="center"> |
| | | <el-table-column fixed="right" label="操作" min-width="120" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">领用</el-button> |
| | | <el-button link type="primary" size="small" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">冻结</el-button> |
| | |
| | | |
| | | <script setup> |
| | | import pagination from '@/components/PIMTable/Pagination.vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance, defineAsyncComponent } from 'vue' |
| | | import {ElMessage, ElMessageBox} from "element-plus"; |
| | | import { getStockInventoryListPage } from "@/api/inventoryManagement/stockInventory.js"; |
| | | const NewStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/New.vue")); |
| | |
| | | }) |
| | | const { searchForm } = toRefs(data) |
| | | |
| | | // 成品(2) / 其他产品(原材料1、半成品3),与产品类型字典一致;分页接口需支持 productType 或 productTypes |
| | | const productScope = ref('成品') |
| | | |
| | | const getProductScopeParams = () => { |
| | | return { productScope: productScope.value } |
| | | } |
| | | |
| | | const onProductScopeChange = () => { |
| | | page.current = 1 |
| | | getList() |
| | | } |
| | | |
| | | // 查询列表 |
| | | /** 搜索按钮操作 */ |
| | | const handleQuery = () => { |
| | |
| | | } |
| | | const getList = () => { |
| | | tableLoading.value = true |
| | | getStockInventoryListPage({ ...searchForm.value, ...page }).then(res => { |
| | | getStockInventoryListPage({ ...searchForm.value, ...page, ...getProductScopeParams() }).then(res => { |
| | | tableLoading.value = false |
| | | tableData.value = res.data.records |
| | | total.value = res.data.total |
| | |
| | | type: 'warning', |
| | | } |
| | | ).then(() => { |
| | | proxy.download("/stockInventory/exportStockInventory", {}, '合格库存信息.xlsx') |
| | | proxy.download("/stockInventory/exportStockInventory", { ...searchForm.value, ...getProductScopeParams() }, '合格库存信息.xlsx') |
| | | }).catch(() => { |
| | | proxy.$modal.msg("已取消") |
| | | }) |
| | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .search_form--wrap { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | align-items: center; |
| | | gap: 12px 16px; |
| | | } |
| | | |
| | | .search-field { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | flex: 0 1 auto; |
| | | min-width: 0; |
| | | gap: 8px; |
| | | |
| | | .search_title { |
| | | flex-shrink: 0; |
| | | white-space: nowrap; |
| | | text-align: right; |
| | | min-width: 4.5em; |
| | | } |
| | | |
| | | .search-input { |
| | | width: 200px; |
| | | } |
| | | |
| | | .qualified-product-scope { |
| | | flex-shrink: 0; |
| | | } |
| | | } |
| | | |
| | | .search-field--actions { |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .qualified-product-scope { |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | :deep(.row-low-stock td) { |
| | | background-color: #fde2e2; |
| | | color: #c45656; |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title ml10">产品大类:</span> |
| | | <el-input v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请输入" |
| | | clearable/> |
| | | <span class="search_title ml10" style="margin-left: 20px">规格型号:</span> |
| | | <el-input v-model="searchForm.model" |
| | | style="width: 240px" |
| | | placeholder="请输入" |
| | | clearable/> |
| | | <span class="search_title ml10" style="margin-left: 20px">UID码:</span> |
| | | <el-input v-model="searchForm.uidNo" |
| | | style="width: 200px" |
| | | placeholder="请输入" |
| | | clearable/> |
| | | <span class="search_title ml10" style="margin-left: 20px">批次号:</span> |
| | | <el-input v-model="searchForm.batchNo" |
| | | style="width: 200px" |
| | | placeholder="请输入" |
| | | clearable/> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button> |
| | | <div class="search_form search_form--wrap"> |
| | | <div class="search-field"> |
| | | <span class="search_title">产品名称:</span> |
| | | <el-input |
| | | v-model="searchForm.productName" |
| | | class="search-input" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="isShowNewModal = true">新增库存</el-button> |
| | | <div class="search-field"> |
| | | <span class="search_title">规格型号:</span> |
| | | <el-input |
| | | v-model="searchForm.model" |
| | | class="search-input" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </div> |
| | | <div class="search-field"> |
| | | <span class="search_title">UID码:</span> |
| | | <el-input |
| | | v-model="searchForm.uidNo" |
| | | class="search-input" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </div> |
| | | <div class="search-field"> |
| | | <span class="search_title">批次号:</span> |
| | | <el-input |
| | | v-model="searchForm.batchNo" |
| | | class="search-input" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </div> |
| | | <div class="search-field search-field--actions"> |
| | | <el-button type="primary" @click="handleQuery">搜索</el-button> |
| | | <el-button type="primary" @click="isShowNewModal = true">新增库存</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | </div> |
| | | </div> |
| | |
| | | <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip /> |
| | | <el-table-column label="备注" prop="remark" show-overflow-tooltip /> |
| | | <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="操作" min-width="60" align="center"> |
| | | <el-table-column fixed="right" label="操作" min-width="120" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">领用</el-button> |
| | | <el-button link type="primary" size="small" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">冻结</el-button> |
| | |
| | | |
| | | <script setup> |
| | | import pagination from '@/components/PIMTable/Pagination.vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance, defineAsyncComponent } from 'vue' |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { getStockUninventoryListPage } from "@/api/inventoryManagement/stockUninventory.js"; |
| | | const NewStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/New.vue")); |
| | |
| | | const expandedRowKeys = ref([]) |
| | | |
| | | // 表格行类名 |
| | | const tableRowClassName = ({ row }) => { |
| | | // const stock = Number(row?.unLockedQuantity ?? 0); |
| | | // const warn = Number(row?.warnNum ?? 0); |
| | | // if (!Number.isFinite(stock) || !Number.isFinite(warn)) { |
| | | // return ''; |
| | | // } |
| | | // return stock < warn ? 'row-low-stock' : ''; |
| | | const tableRowClassName = () => { |
| | | return ''; |
| | | }; |
| | | |
| | | // 导出 |
| | |
| | | type: 'warning', |
| | | } |
| | | ).then(() => { |
| | | proxy.download("/stockUninventory/exportStockUninventory", {}, '不合格库存信息.xlsx') |
| | | proxy.download("/stockUninventory/exportStockUninventory", { ...searchForm.value }, '不合格库存信息.xlsx') |
| | | }).catch(() => { |
| | | proxy.$modal.msg("已取消") |
| | | }) |
| | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .search_form--wrap { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | align-items: center; |
| | | gap: 12px 16px; |
| | | } |
| | | |
| | | .search-field { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | flex: 0 1 auto; |
| | | min-width: 0; |
| | | gap: 8px; |
| | | |
| | | .search_title { |
| | | flex-shrink: 0; |
| | | white-space: nowrap; |
| | | text-align: right; |
| | | min-width: 4.5em; |
| | | } |
| | | |
| | | .search-input { |
| | | width: 200px; |
| | | } |
| | | } |
| | | |
| | | .search-field--actions { |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | |
| | | :deep(.row-low-stock td) { |
| | | background-color: #fde2e2; |
| | | color: #c45656; |
| | |
| | | prop="supplierName" |
| | | width="160" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="项目名称" |
| | | <!-- <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> |
| | | show-overflow-tooltip /> --> |
| | | <el-table-column label="签订日期" |
| | | prop="executionDate" |
| | | width="100" |
| | |
| | | </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%" |
| | |
| | | clearable /> |
| | | </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-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | |
| | | </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"> |
| | |
| | | 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 |
| | |
| | | |
| | | 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([]); |
| | |
| | | purchaseContractNumber: [ |
| | | { required: true, message: "请输入", trigger: "blur" }, |
| | | ], |
| | | approverId: [ |
| | | { required: true, message: "请选择审批人", trigger: "change" }, |
| | | ], |
| | | projectName: [ |
| | | { required: true, message: "请输入项目名称", trigger: "blur" }, |
| | | ], |
| | |
| | | productRules: { |
| | | productId: [{ required: true, message: "请选择", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "请选择", trigger: "change" }], |
| | | batchNo: [{ required: true, message: "请输入批次号", trigger: "blur" }], |
| | | unit: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | taxInclusiveUnitPrice: [ |
| | |
| | | } |
| | | |
| | | 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); |
| | |
| | | }; |
| | | // 打开弹框 |
| | | 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) { |
| | | // 新增时,需要从每个产品对象中删除 id 字段 |
| | | let processedProductData = productData.value; |
| | |
| | | } |
| | | form.value.tempFileIds = tempFileIds; |
| | | form.value.type = 2; |
| | | form.value.approveUserIds = approveUserIds; |
| | | |
| | | // 如果salesLedgerId为空,则不传递salesContractNo |
| | | if (!form.value.salesLedgerId) { |
| | |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | // 重置审批人节点(默认一个空节点) |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | nextApproverId = 2; |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | // 打开产品弹框 |
| | |
| | | .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%; |
| | | } |
| | | } |
| | | |
| | | // 删除图标样式 |
| | |
| | | const product = products[0]; |
| | | formState.value.productId = product.productId; |
| | | formState.value.productName = product.productName; |
| | | if (product.parentName === '一类产品') { |
| | | formState.value.manufacturingTeam = product.parentName.charAt(0) + '类车间'; |
| | | } |
| | | formState.value.manufacturingTeam = product.parentName.charAt(0) + '类车间'; |
| | | |
| | | formState.value.productModelName = product.model; |
| | | formState.value.productModelId = product.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="form.unit" placeholder="请输入" disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="批号:" prop="batchNo"> |
| | | <el-input |
| | | v-model="form.batchNo" |
| | | placeholder="请输入" |
| | | clearable |
| | | :disabled="operationType === 'edit'" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | <el-input v-model="form.uidNo" placeholder="请输入" disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检品数量:" prop="inspectedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inspectedQuantity" placeholder="请输入" clearable :precision="2"/> |
| | |
| | | <el-col :span="12"> |
| | | <el-form-item label="总数量:" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2" :disabled="quantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检品数量:" prop="inspectedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inspectedQuantity" placeholder="请输入,不大于总数量" clearable :precision="2" :disabled="quantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验用粉剂/液情况:" prop="inspectMaterialCondition"> |
| | | <el-radio-group v-model="form.inspectMaterialCondition"> |
| | | <el-radio label="检验用粉剂情况">检验用粉剂情况</el-radio> |
| | | <el-radio label="检验用液剂情况">检验用液剂情况</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="生产日期:" prop="productionDate"> |
| | | <el-date-picker |
| | | v-model="form.productionDate" |
| | | type="date" |
| | | placeholder="请选择日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="calculateValidityDate" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="有效期:" prop="validityDate"> |
| | | <el-date-picker |
| | | v-model="form.validityDate" |
| | | type="date" |
| | | placeholder="自动计算或手动选择" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-select> |
| | | </template> |
| | | <template #deviceStatus="{ row }"> |
| | | <el-tag v-if="row.deviceStatus" :type="getDeviceStatusType(row.deviceStatus)"> |
| | | {{ row.deviceStatus }} |
| | | </el-tag> |
| | | <span v-else style="color: #999">-</span> |
| | | <el-select |
| | | v-model="row.deviceStatus" |
| | | placeholder="请选择" |
| | | default-first-option |
| | | clearable |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="正常" value="正常" /> |
| | | <el-option label="停机" value="停机" /> |
| | | <el-option label="运行" value="运行" /> |
| | | <el-option label="维修" value="维修" /> |
| | | <el-option label="/" value="/" /> |
| | | </el-select> |
| | | </template> |
| | | <template #result="{ row }"> |
| | | <el-input v-model="row.result" placeholder="请输入" clearable /> |
| | |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | |
| | | const validateBatchNo = (rule, value, callback) => { |
| | | if (value === undefined || value === null || String(value).trim() === '') { |
| | | callback(new Error('请输入批号')); |
| | | return; |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | |
| | | testStandardId: "", |
| | | unit: "", |
| | | uidNo: "", |
| | | batchNo: "", |
| | | inspectedQuantity: "", |
| | | quantity: "", |
| | | inspectedQuantity: "", |
| | | inspectMaterialCondition: "", |
| | | productionDate: "", |
| | | validityDate: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | }, |
| | |
| | | unit: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | inspectedQuantity: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | inspectedQuantity: [ |
| | | { required: true, message: "请输入检品数量", trigger: "blur" }, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (value !== '' && value !== null && value !== undefined) { |
| | | const qty = Number(form.value.quantity); |
| | | const inspectedQty = Number(value); |
| | | if (!isNaN(qty) && !isNaN(inspectedQty) && inspectedQty > qty) { |
| | | callback(new Error("检品数量不能大于总数量")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: "blur" |
| | | } |
| | | ], |
| | | checkCompany: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | batchNo: [{ required: true, validator: validateBatchNo, trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请输入", trigger: "change" }], |
| | | }, |
| | | }); |
| | |
| | | prop: "standardValue", |
| | | width: 180 |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | width: 80 |
| | | }, |
| | | { |
| | | label: "检测器具", |
| | | prop: "instrument", |
| | | dataType: 'slot', |
| | | slot: 'instrument', |
| | | width: 220 |
| | | }, |
| | | { |
| | | label: "设备状态", |
| | | prop: "deviceStatus", |
| | | dataType: 'slot', |
| | | slot: 'deviceStatus', |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "检测结果", |
| | | prop: "result", |
| | | dataType: 'slot', |
| | | slot: 'result', |
| | | minWidth: 150 |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | width: 70 |
| | | }, |
| | | { |
| | | label: "检测器具", |
| | | prop: "instrument", |
| | | dataType: 'slot', |
| | | slot: 'instrument', |
| | | width: 220 |
| | | }, |
| | | { |
| | | label: "设备状态", |
| | | prop: "deviceStatus", |
| | | dataType: 'slot', |
| | | slot: 'deviceStatus', |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "检测结果", |
| | | prop: "result", |
| | | dataType: 'slot', |
| | | slot: 'result', |
| | | width: 150 |
| | | }, |
| | | { |
| | | label: "结果判断", |
| | | prop: "resultJudgment", |
| | |
| | | form.value.productModelId = undefined; |
| | | form.value.unit = undefined; |
| | | form.value.uidNo = undefined; |
| | | form.value.batchNo = ""; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | |
| | | form.value.model = modelOptions.value.find(item => item.id == value)?.model || ''; |
| | | form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || ''; |
| | | form.value.uidNo = modelOptions.value.find(item => item.id == value)?.uidNo || ''; |
| | | // 选择规格型号后,如果已有生产日期则重新计算有效期 |
| | | if (form.value.productionDate) { |
| | | calculateValidityDate(); |
| | | } |
| | | } |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | |
| | | }) |
| | | } |
| | | |
| | | // 计算有效期(生产日期 + 规格型号中的有效期) |
| | | const calculateValidityDate = async () => { |
| | | if (!form.value.productionDate) { |
| | | form.value.validityDate = ''; |
| | | return; |
| | | } |
| | | // 获取规格型号的有效期 |
| | | const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId); |
| | | if (selectedModel && selectedModel.validityPeriod) { |
| | | const productionDate = new Date(form.value.productionDate); |
| | | const validityPeriod = parseFloat(selectedModel.validityPeriod); |
| | | const validityDate = new Date(productionDate); |
| | | validityDate.setFullYear(validityDate.getFullYear() + Math.floor(validityPeriod)); |
| | | validityDate.setMonth(validityDate.getMonth() + Math.round((validityPeriod % 1) * 12)); |
| | | form.value.validityDate = validityDate.toISOString().split('T')[0]; |
| | | } |
| | | }; |
| | | |
| | | // 获取设备台账列表 |
| | | const loadDeviceList = () => { |
| | | qualityInspectParamDeviceList().then(res => { |
| | |
| | | import {ElMessageBox} from "element-plus"; |
| | | import { |
| | | downloadQualityInspect, |
| | | downloadOutReport, |
| | | qualityInspectDel, |
| | | qualityInspectListPage, qualityInspectUpdate, |
| | | submitQualityInspect |
| | |
| | | return false; |
| | | } |
| | | }, |
| | | { |
| | | /*{ |
| | | name: "填写检验记录", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | |
| | | } |
| | | return false; |
| | | } |
| | | }, |
| | | },*/ |
| | | { |
| | | name: "附件", |
| | | type: "text", |
| | |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | downLoadFile(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "报告", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | downloadReport(row); |
| | | }, |
| | | }, |
| | | ], |
| | |
| | | type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
| | | }) |
| | | const downloadUrl = window.URL.createObjectURL(blob) |
| | | |
| | | |
| | | const link = document.createElement('a') |
| | | link.href = downloadUrl |
| | | link.download = '原材料检验报告.docx' |
| | | document.body.appendChild(link) |
| | | link.click() |
| | | |
| | | |
| | | document.body.removeChild(link) |
| | | window.URL.revokeObjectURL(downloadUrl) |
| | | }) |
| | | }; |
| | | |
| | | const downloadReport = (row) => { |
| | | downloadOutReport({ id: row.id }).then((blobData) => { |
| | | const blob = new Blob([blobData], { |
| | | type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
| | | }) |
| | | const downloadUrl = window.URL.createObjectURL(blob) |
| | | |
| | | const link = document.createElement('a') |
| | | link.href = downloadUrl |
| | | link.download = '出库检验报告.docx' |
| | | document.body.appendChild(link) |
| | | link.click() |
| | | |
| | | document.body.removeChild(link) |
| | | window.URL.revokeObjectURL(downloadUrl) |
| | | }) |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="批号:" prop="batchNo" :required="operationType === 'add'"> |
| | | <el-input |
| | | v-model="form.batchNo" |
| | | placeholder="请输入" |
| | | clearable |
| | | :disabled="operationType === 'edit'" |
| | | /> |
| | | </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="form.unit" placeholder="请输入" disabled/> |
| | | </el-form-item> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, toRefs} from "vue"; |
| | | import {ref, reactive, toRefs, getCurrentInstance} from "vue"; |
| | | import {modelList, productTreeList} from "@/api/basicData/product.js"; |
| | | import { |
| | | getQualityUnqualifiedInfo, |
| | |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | |
| | | const validateBatchNo = (rule, value, callback) => { |
| | | if (operationType.value !== 'add') { |
| | | callback(); |
| | | return; |
| | | } |
| | | if (value === undefined || value === null || String(value).trim() === '') { |
| | | callback(new Error('请输入批号')); |
| | | return; |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | const { rejection_handling } = proxy.useDict("rejection_handling") |
| | | const data = reactive({ |
| | | form: { |
| | |
| | | productId: "", |
| | | model: "", |
| | | uidNo: "", |
| | | batchNo: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | |
| | | checkCompany: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | checkResult: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | dealName: [{ required: true, message: "请选择处理人", trigger: "change" }], |
| | | batchNo: [{ validator: validateBatchNo, trigger: "blur" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | |
| | | productId: '', |
| | | model: '', |
| | | uidNo: '', |
| | | batchNo: '', |
| | | unit: '', |
| | | quantity: '', |
| | | productName: '', |
| | |
| | | form.value.model = undefined; |
| | | form.value.unit = undefined; |
| | | form.value.uidNo = undefined; |
| | | form.value.batchNo = ""; |
| | | modelOptions.value = []; |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then((res) => { |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="批号:" prop="batchNo"> |
| | | <el-input v-model="form.batchNo" placeholder="—" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验员:" prop="checkName"> |
| | | <el-input v-model="form.checkName" placeholder="请输入" clearable disabled/> |
| | | </el-form-item> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, toRefs, computed} from "vue"; |
| | | import {ref, reactive, toRefs, computed, getCurrentInstance} from "vue"; |
| | | import {productTreeList} from "@/api/basicData/product.js"; |
| | | import { |
| | | getQualityUnqualifiedInfo, |
| | |
| | | productName: "", |
| | | productId: "", |
| | | model: "", |
| | | batchNo: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | |
| | | <el-input |
| | | v-model="searchForm.productName" |
| | | style="width: 200px" |
| | | placeholder="请输入产品名称搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">新增</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | <!-- <el-button type="primary" @click="openForm('add')">新增</el-button> --> |
| | | <!-- <el-button @click="handleOut">导出</el-button> --> |
| | | <el-button type="danger" plain @click="handleDelete">删除</el-button> |
| | | </div> |
| | | </div> |
| | |
| | | prop: "uidNo", |
| | | }, |
| | | { |
| | | label: "批号", |
| | | prop: "batchNo", |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-form-item label="总数量:" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2" :disabled="processQuantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检品数量:" prop="inspectedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inspectedQuantity" placeholder="请输入,不大于总数量" clearable :precision="2" :disabled="processQuantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验用粉剂/液情况:" prop="inspectMaterialCondition"> |
| | | <el-radio-group v-model="form.inspectMaterialCondition"> |
| | | <el-radio label="粉剂">粉剂</el-radio> |
| | | <el-radio label="液体">液体</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="生产日期:" prop="productionDate"> |
| | | <el-date-picker |
| | | v-model="form.productionDate" |
| | | type="date" |
| | | placeholder="请选择日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="calculateValidityDate" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="有效期:" prop="validityDate"> |
| | | <el-date-picker |
| | | v-model="form.validityDate" |
| | | type="date" |
| | | placeholder="自动计算或手动选择" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | <el-input v-model="form.uidNo" placeholder="请输入" disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="批号:" prop="batchNo"> |
| | | <el-input |
| | | v-model="form.batchNo" |
| | | placeholder="请输入" |
| | | clearable |
| | | :disabled="operationType === 'edit'" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测单位:" prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" placeholder="请输入" clearable/> |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验员:" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="请选择" clearable> |
| | |
| | | </el-select> |
| | | </template> |
| | | <template #deviceStatus="{ row }"> |
| | | <el-tag v-if="row.deviceStatus" :type="getDeviceStatusType(row.deviceStatus)"> |
| | | {{ row.deviceStatus }} |
| | | </el-tag> |
| | | <span v-else style="color: #999">-</span> |
| | | <el-select |
| | | v-model="row.deviceStatus" |
| | | placeholder="请选择" |
| | | default-first-option |
| | | clearable |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="正常" value="正常" /> |
| | | <el-option label="停机" value="停机" /> |
| | | <el-option label="运行" value="运行" /> |
| | | <el-option label="维修" value="维修" /> |
| | | <el-option label="/" value="/" /> |
| | | </el-select> |
| | | </template> |
| | | <template #result="{ row }"> |
| | | <el-input v-model="row.result" placeholder="请输入" clearable /> |
| | |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | |
| | | const validateBatchNo = (rule, value, callback) => { |
| | | if (value === undefined || value === null || String(value).trim() === '') { |
| | | callback(new Error('请输入批号')); |
| | | return; |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | |
| | | productModelId: "", |
| | | model: "", |
| | | uidNo: "", |
| | | batchNo: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | inspectedQuantity: "", |
| | | inspectMaterialCondition: "", |
| | | productionDate: "", |
| | | validityDate: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | }, |
| | |
| | | testStandardId: [{required: false, message: "请选择指标", trigger: "change"}], |
| | | unit: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | inspectedQuantity: [ |
| | | { required: true, message: "请输入检品数量", trigger: "blur" }, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (value !== '' && value !== null && value !== undefined) { |
| | | const qty = Number(form.value.quantity); |
| | | const inspectedQty = Number(value); |
| | | if (!isNaN(qty) && !isNaN(inspectedQty) && inspectedQty > qty) { |
| | | callback(new Error("检品数量不能大于总数量")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: "blur" |
| | | } |
| | | ], |
| | | checkCompany: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | batchNo: [{ required: true, validator: validateBatchNo, trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请输入", trigger: "change" }], |
| | | }, |
| | | }); |
| | |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | width: 80 |
| | | width: 70 |
| | | }, |
| | | { |
| | | label: "检测器具", |
| | |
| | | prop: "result", |
| | | dataType: 'slot', |
| | | slot: 'result', |
| | | minWidth: 150 |
| | | width: 150 |
| | | }, |
| | | { |
| | | label: "结果判断", |
| | |
| | | testStandardId: "", |
| | | unit: "", |
| | | uidNo: "", |
| | | batchNo: "", |
| | | quantity: "", |
| | | inspectedQuantity: "", |
| | | inspectMaterialCondition: "", |
| | | productionDate: "", |
| | | validityDate: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | } |
| | |
| | | form.value.productModelId = undefined; |
| | | form.value.unit = undefined; |
| | | form.value.uidNo = undefined; |
| | | form.value.batchNo = ""; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | |
| | | form.value.model = modelOptions.value.find(item => item.id == value)?.model || ''; |
| | | form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || ''; |
| | | form.value.uidNo = modelOptions.value.find(item => item.id == value)?.uidNo || ''; |
| | | // 选择规格型号后,如果已有生产日期则重新计算有效期 |
| | | if (form.value.productionDate) { |
| | | calculateValidityDate(); |
| | | } |
| | | } |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | |
| | | } |
| | | }; |
| | | |
| | | // 计算有效期(生产日期 + 规格型号中的有效期) |
| | | const calculateValidityDate = async () => { |
| | | if (!form.value.productionDate) { |
| | | form.value.validityDate = ''; |
| | | return; |
| | | } |
| | | // 获取规格型号的有效期 |
| | | const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId); |
| | | if (selectedModel && selectedModel.validityPeriod) { |
| | | const productionDate = new Date(form.value.productionDate); |
| | | const validityPeriod = parseFloat(selectedModel.validityPeriod); |
| | | const validityDate = new Date(productionDate); |
| | | validityDate.setFullYear(validityDate.getFullYear() + Math.floor(validityPeriod)); |
| | | validityDate.setMonth(validityDate.getMonth() + Math.round((validityPeriod % 1) * 12)); |
| | | form.value.validityDate = validityDate.toISOString().split('T')[0]; |
| | | } |
| | | }; |
| | | |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | |
| | | prop: "uidNo", |
| | | }, |
| | | { |
| | | label: "批号", |
| | | prop: "batchNo", |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | |
| | | return false; |
| | | } |
| | | }, |
| | | { |
| | | /*{ |
| | | name: "填写检验记录", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | |
| | | } |
| | | return false; |
| | | } |
| | | }, |
| | | },*/ |
| | | { |
| | | name: "附件", |
| | | type: "text", |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-form-item label="总数量:" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" |
| | | clearable :precision="2" :disabled="supplierQuantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检品数量:" prop="inspectedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inspectedQuantity" placeholder="请输入,不大于总数量" |
| | | clearable :precision="2" :disabled="supplierQuantityDisabled"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验用粉剂/液情况:" prop="inspectMaterialCondition"> |
| | | <el-radio-group v-model="form.inspectMaterialCondition"> |
| | | <el-radio label="粉剂">粉剂</el-radio> |
| | | <el-radio label="液体">液体</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="生产日期:" prop="productionDate"> |
| | | <el-date-picker |
| | | v-model="form.productionDate" |
| | | type="date" |
| | | placeholder="请选择日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="calculateValidityDate" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="有效期:" prop="validityDate"> |
| | | <el-date-picker |
| | | v-model="form.validityDate" |
| | | type="date" |
| | | placeholder="自动计算或手动选择" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测单位:" prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" placeholder="请输入" clearable/> |
| | | <el-form-item label="批号:" prop="batchNo"> |
| | | <el-input |
| | | v-model="form.batchNo" |
| | | placeholder="请输入" |
| | | clearable |
| | | :disabled="operationType === 'edit'" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测单位:" prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" placeholder="请输入" clearable/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测结果:" prop="checkResult"> |
| | | <el-select v-model="form.checkResult"> |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验员:" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="请选择" clearable style="width: 100%"> |
| | |
| | | </el-select> |
| | | </template> |
| | | <template #deviceStatus="{ row }"> |
| | | <el-tag v-if="row.deviceStatus" :type="getDeviceStatusType(row.deviceStatus)"> |
| | | {{ row.deviceStatus }} |
| | | </el-tag> |
| | | <span v-else style="color: #999">-</span> |
| | | <el-select |
| | | v-model="row.deviceStatus" |
| | | placeholder="请选择" |
| | | default-first-option |
| | | clearable |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="正常" value="正常" /> |
| | | <el-option label="停机" value="停机" /> |
| | | <el-option label="运行" value="运行" /> |
| | | <el-option label="维修" value="维修" /> |
| | | <el-option label="/" value="/" /> |
| | | </el-select> |
| | | </template> |
| | | <template #result="{ row }"> |
| | | <el-input v-model="row.result" placeholder="请输入" clearable /> |
| | |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | |
| | | const validateBatchNo = (rule, value, callback) => { |
| | | if (value === undefined || value === null || String(value).trim() === '') { |
| | | callback(new Error('请输入批号')); |
| | | return; |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | |
| | | productModelId: "", |
| | | model: "", |
| | | uidNo: "", |
| | | batchNo: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | inspectedQuantity: "", |
| | | inspectMaterialCondition: "", |
| | | productionDate: "", |
| | | validityDate: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | }, |
| | |
| | | testStandardId: [{required: false, message: "请选择指标", trigger: "change"}], |
| | | unit: [{required: false, message: "请输入", trigger: "blur"}], |
| | | quantity: [{required: true, message: "请输入", trigger: "blur"}], |
| | | inspectedQuantity: [ |
| | | {required: true, message: "请输入检品数量", trigger: "blur"}, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (value !== '' && value !== null && value !== undefined) { |
| | | const qty = Number(form.value.quantity); |
| | | const inspectedQty = Number(value); |
| | | if (!isNaN(qty) && !isNaN(inspectedQty) && inspectedQty > qty) { |
| | | callback(new Error("检品数量不能大于总数量")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: "blur" |
| | | } |
| | | ], |
| | | checkCompany: [{required: false, message: "请输入", trigger: "blur"}], |
| | | batchNo: [{ required: true, validator: validateBatchNo, trigger: "blur" }], |
| | | checkResult: [{required: true, message: "请选择检测结果", trigger: "change"}], |
| | | }, |
| | | }); |
| | |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | width: 80 |
| | | width: 70 |
| | | }, |
| | | { |
| | | label: "检测器具", |
| | |
| | | prop: "result", |
| | | dataType: 'slot', |
| | | slot: 'result', |
| | | minWidth: 150 |
| | | width: 150 |
| | | }, |
| | | { |
| | | label: "结果判断", |
| | |
| | | productModelId: "", |
| | | model: "", |
| | | uidNo: "", |
| | | batchNo: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | inspectedQuantity: "", |
| | | inspectMaterialCondition: "", |
| | | productionDate: "", |
| | | validityDate: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | } |
| | |
| | | form.value.productModelId = undefined; |
| | | form.value.unit = undefined; |
| | | form.value.uidNo = undefined; |
| | | form.value.batchNo = ""; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | |
| | | form.value.model = modelOptions.value.find(item => item.id == value)?.model || ''; |
| | | form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || ''; |
| | | form.value.uidNo = modelOptions.value.find(item => item.id == value)?.uidNo || ''; |
| | | // 选择规格型号后,如果已有生产日期则重新计算有效期 |
| | | if (form.value.productionDate) { |
| | | calculateValidityDate(); |
| | | } |
| | | } |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | |
| | | } |
| | | }; |
| | | |
| | | // 计算有效期(生产日期 + 规格型号中的有效期) |
| | | const calculateValidityDate = async () => { |
| | | if (!form.value.productionDate) { |
| | | form.value.validityDate = ''; |
| | | return; |
| | | } |
| | | // 获取规格型号的有效期 |
| | | const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId); |
| | | if (selectedModel && selectedModel.validityPeriod) { |
| | | const productionDate = new Date(form.value.productionDate); |
| | | const validityPeriod = parseFloat(selectedModel.validityPeriod); |
| | | const validityDate = new Date(productionDate); |
| | | validityDate.setFullYear(validityDate.getFullYear() + Math.floor(validityPeriod)); |
| | | validityDate.setMonth(validityDate.getMonth() + Math.round((validityPeriod % 1) * 12)); |
| | | form.value.validityDate = validityDate.toISOString().split('T')[0]; |
| | | } |
| | | }; |
| | | |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | |
| | | prop: "uidNo", |
| | | }, |
| | | { |
| | | label: "批号", |
| | | prop: "batchNo", |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | |
| | | return false; |
| | | } |
| | | }, |
| | | { |
| | | /*{ |
| | | name: "填写检验记录", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | |
| | | } |
| | | return false; |
| | | } |
| | | }, |
| | | },*/ |
| | | { |
| | | name: "附件", |
| | | type: "text", |
| | |
| | | <el-table-column label="发货车牌号" prop="shippingCarNumber" show-overflow-tooltip /> |
| | | <el-table-column label="快递公司" prop="expressCompany" show-overflow-tooltip /> |
| | | <el-table-column label="快递单号" prop="expressNumber" show-overflow-tooltip /> |
| | | <el-table-column label="审核状态" prop="status" align="center" width="120"> |
| | | <el-table-column label="发货状态" prop="status" align="center" width="120"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getApprovalStatusType(scope.row.status)"> |
| | | {{ getApprovalStatusText(scope.row.status) }} |
| | | <el-tag :type="getShippingStatusType(scope.row.status)"> |
| | | {{ getShippingStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="!isApproved(scope.row.status)" |
| | | :disabled="isShipped(scope.row.status)" |
| | | @click="openForm('edit', scope.row)">补充发货信息</el-button> |
| | | <el-button |
| | | link |
| | |
| | | link |
| | | type="danger" |
| | | size="small" |
| | | :disabled="isApproving(scope.row.status)" |
| | | :disabled="isShipped(scope.row.status)" |
| | | @click="handleDeleteSingle(scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | <el-descriptions-item label="规格型号">{{ detailRow.specificationModel || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="发货类型">{{ detailRow.type || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="发货日期">{{ detailRow.shippingDate || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="审核状态">{{ getApprovalStatusText(detailRow.status) }}</el-descriptions-item> |
| | | <el-descriptions-item label="发货状态">{{ getShippingStatusText(detailRow.status) }}</el-descriptions-item> |
| | | <el-descriptions-item label="发货车牌号">{{ detailRow.shippingCarNumber || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="快递公司">{{ detailRow.expressCompany || '--' }}</el-descriptions-item> |
| | | <el-descriptions-item label="快递单号" :span="2">{{ detailRow.expressNumber || '--' }}</el-descriptions-item> |
| | |
| | | |
| | | // 打开弹框 |
| | | const openForm = async (type, row) => { |
| | | // 补充发货信息:仅“审核通过”允许编辑 |
| | | if (type === 'edit' && row && !isApproved(row.status)) { |
| | | proxy.$modal.msgWarning("只有审核通过的数据才可以补充发货信息"); |
| | | // 补充发货信息:仅“未发货”允许编辑 |
| | | if (type === 'edit' && row && isShipped(row.status)) { |
| | | proxy.$modal.msgWarning("已发货的数据不能补充发货信息"); |
| | | return; |
| | | } |
| | | |
| | |
| | | return; |
| | | } |
| | | |
| | | // 检查选中的行是否有"审核中"状态 |
| | | const approvingRows = selectedRows.value.filter(row => isApproving(row.status)); |
| | | if (approvingRows.length > 0) { |
| | | proxy.$modal.msgWarning("审核中的数据不能删除"); |
| | | // 已发货数据不允许删除 |
| | | const shippedRows = selectedRows.value.filter(row => isShipped(row.status)); |
| | | if (shippedRows.length > 0) { |
| | | proxy.$modal.msgWarning("已发货的数据不能删除"); |
| | | return; |
| | | } |
| | | |
| | |
| | | |
| | | // 单个删除 |
| | | const handleDeleteSingle = (row) => { |
| | | // 检查是否为"审核中"状态 |
| | | if (isApproving(row.status)) { |
| | | proxy.$modal.msgWarning("审核中的数据不能删除"); |
| | | // 已发货数据不允许删除 |
| | | if (isShipped(row.status)) { |
| | | proxy.$modal.msgWarning("已发货的数据不能删除"); |
| | | return; |
| | | } |
| | | |
| | |
| | | } |
| | | }; |
| | | |
| | | // 获取审核状态文本 |
| | | const getApprovalStatusText = (status) => { |
| | | // 获取发货状态文本 |
| | | const getShippingStatusText = (status) => { |
| | | if (status === null || status === undefined || status === '') { |
| | | return '待审核'; |
| | | return '未发货'; |
| | | } |
| | | // 如果是数字 |
| | | if (typeof status === 'number') { |
| | | const statusMap = { |
| | | 0: '待审核', |
| | | 1: '审核中', |
| | | 2: '审核拒绝', |
| | | 3: '审核通过' |
| | | 0: '未发货', |
| | | 1: '已发货', |
| | | }; |
| | | return statusMap[status] || '待审核'; |
| | | return statusMap[status] || '未发货'; |
| | | } |
| | | // 如果是字符串,直接返回或映射 |
| | | const statusStr = String(status).trim(); |
| | | const statusTextMap = { |
| | | '待审核': '待审核', |
| | | '审核中': '审核中', |
| | | '审核拒绝': '审核拒绝', |
| | | '审核通过': '审核通过', |
| | | '0': '待审核', |
| | | '1': '审核中', |
| | | '2': '审核拒绝', |
| | | '3': '审核通过' |
| | | '未发货': '未发货', |
| | | '已发货': '已发货', |
| | | '0': '未发货', |
| | | '1': '已发货', |
| | | }; |
| | | return statusTextMap[statusStr] || statusStr || '待审核'; |
| | | return statusTextMap[statusStr] || statusStr || '未发货'; |
| | | }; |
| | | |
| | | // 获取审核状态标签类型(颜色) |
| | | const getApprovalStatusType = (status) => { |
| | | // 获取发货状态标签类型(颜色) |
| | | const getShippingStatusType = (status) => { |
| | | if (status === null || status === undefined || status === '') { |
| | | return 'info'; |
| | | } |
| | | // 如果是数字 |
| | | if (typeof status === 'number') { |
| | | const typeMap = { |
| | | 0: 'info', // 待审核 - 灰色 |
| | | 1: 'warning', // 审核中 - 黄色 |
| | | 2: 'danger', // 审核拒绝 - 红色 |
| | | 3: 'success' // 审核通过 - 绿色 |
| | | 0: 'info', // 未发货 - 灰色 |
| | | 1: 'success', // 已发货 - 绿色 |
| | | }; |
| | | return typeMap[status] || 'info'; |
| | | } |
| | | // 如果是字符串 |
| | | const statusStr = String(status).trim(); |
| | | const typeTextMap = { |
| | | '待审核': 'info', |
| | | '审核中': 'warning', |
| | | '审核拒绝': 'danger', |
| | | '审核通过': 'success', |
| | | '未发货': 'info', |
| | | '已发货': 'success', |
| | | '0': 'info', |
| | | '1': 'warning', |
| | | '2': 'danger', |
| | | '3': 'success' |
| | | '1': 'success', |
| | | }; |
| | | return typeTextMap[statusStr] || 'info'; |
| | | }; |
| | | |
| | | // 检查审核状态是否为"审核通过" |
| | | const isApproved = (status) => { |
| | | // 是否已发货 |
| | | const isShipped = (status) => { |
| | | if (status === null || status === undefined || status === '') { |
| | | return false; |
| | | } |
| | | // 如果是数字,3 表示审核通过 |
| | | if (typeof status === 'number') { |
| | | return status === 3; |
| | | } |
| | | // 如果是字符串 |
| | | const statusStr = String(status).trim(); |
| | | return statusStr === '审核通过' || statusStr === '3'; |
| | | }; |
| | | |
| | | // 检查审核状态是否为"审核中" |
| | | const isApproving = (status) => { |
| | | if (status === null || status === undefined || status === '') { |
| | | return false; |
| | | } |
| | | // 如果是数字,1 表示审核中 |
| | | if (typeof status === 'number') { |
| | | return status === 1; |
| | | } |
| | | // 如果是字符串 |
| | | const statusStr = String(status).trim(); |
| | | return statusStr === '审核中' || statusStr === '1'; |
| | | return statusStr === '已发货' || statusStr === '1'; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | |
| | | { label: "销售单号", prop: "salesContractNo", minWidth: 160 }, |
| | | { label: "业务员", prop: "salesman", minWidth: 120 }, |
| | | { label: "关联出库单号", prop: "shippingNo", minWidth: 170 }, |
| | | { label: "项目名称", prop: "projectName", minWidth: 180 }, |
| | | // { label: "项目名称", prop: "projectName", minWidth: 180 }, |
| | | { label: "项目阶段", prop: "projectStage", minWidth: 120 }, |
| | | { label: "制单人", prop: "maker", minWidth: 120 }, |
| | | { label: "结算人", prop: "settler", minWidth: 120 }, |
| | |
| | | <el-button type="primary" plain @click="handleImport">导入</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">删除</el-button> |
| | | <el-button type="primary" plain @click="handlePrint">打印</el-button> |
| | | <!-- <el-button type="primary" plain @click="handlePrint">打印</el-button> --> |
| | | </div> |
| | | </div> |
| | | <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" |
| | |
| | | <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip /> |
| | | <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip /> |
| | | <!-- <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip /> --> |
| | | <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip /> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项目名称:" prop="projectName"> |
| | | <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <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 :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="付款方式"> |
| | | <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="录入人:" prop="entryPerson"> |
| | |
| | | <el-table-column prop="customer" label="客户名称" min-width="220" show-overflow-tooltip /> |
| | | <el-table-column prop="salesperson" label="业务员" width="120" show-overflow-tooltip /> |
| | | <el-table-column prop="quotationDate" label="报价日期" width="140" /> |
| | | <el-table-column prop="status" label="审批状态" width="120" align="center" /> |
| | | <!-- <el-table-column prop="status" label="审批状态" width="120" align="center" /> --> |
| | | <el-table-column prop="totalAmount" label="报价金额(元)" width="160" align="right"> |
| | | <template #default="scope"> |
| | | {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }} |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 审批人选择(仿协同审批里的审批人节点选择) --> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item> |
| | | <template #label> |
| | | <span>审批人选择:</span> |
| | | <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button> |
| | | </template> |
| | | <div style="display: flex; align-items: flex-end; flex-wrap: wrap;"> |
| | | <div |
| | | v-for="(node, index) in approverNodes" |
| | | :key="node.id" |
| | | style="margin-right: 20px; text-align: center; margin-bottom: 10px;" |
| | | > |
| | | <div> |
| | | <span>审批人</span> |
| | | → |
| | | </div> |
| | | <el-select |
| | | v-model="node.userId" |
| | | placeholder="选择人员" |
| | | filterable |
| | | style="width: 140px; margin-bottom: 8px;" |
| | | > |
| | | <el-option |
| | | v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" |
| | | /> |
| | | </el-select> |
| | | <div> |
| | | <el-button |
| | | type="danger" |
| | | size="small" |
| | | @click="removeApproverNode(index)" |
| | | v-if="approverNodes.length > 1" |
| | | >删除</el-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | |
| | | const submitDelivery = () => { |
| | | proxy.$refs["deliveryFormRef"].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(","); |
| | | // 保存当前展开的行ID,以便发货后重新加载子表格数据 |
| | | const currentExpandedKeys = [...expandedRowKeys.value]; |
| | | const salesLedgerId = currentDeliveryRow.value.salesLedgerId; |
| | |
| | | salesLedgerId: salesLedgerId, |
| | | salesLedgerProductId: currentDeliveryRow.value.id, |
| | | type: deliveryForm.value.type, |
| | | approveUserIds, |
| | | }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("发货成功"); |
| | |
| | | <el-table-column prop="salesperson" label="业务员" width="100" /> |
| | | <el-table-column prop="quotationDate" label="报价日期" width="120" /> |
| | | <el-table-column prop="validDate" label="有效期至" width="120" /> |
| | | <el-table-column prop="status" label="审批状态" width="120" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)" disable-transitions> |
| | | {{ row.status || '--' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="totalAmount" label="报价金额" width="120"> |
| | | <template #default="scope"> |
| | | ¥{{ scope.row.totalAmount.toFixed(2) }} |
| | |
| | | <el-table-column label="操作" width="200" fixed="right" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="handleView(scope.row)">查看</el-button> |
| | | <el-button link type="primary" @click="handleEdit(scope.row)" :disabled="!['待审批','拒绝'].includes(scope.row.status)">编辑</el-button> |
| | | <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="24"> |
| | | <!-- <el-row :gutter="24"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="付款方式" prop="paymentMethod"> |
| | | <el-input v-model="form.paymentMethod" placeholder="请输入付款方式" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 审批人信息 --> |
| | | <el-card class="form-card" shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header-wrapper"> |
| | | <el-icon class="card-icon"><UserFilled /></el-icon> |
| | | <span class="card-title">审批人选择</span> |
| | | <el-button type="primary" size="small" @click="addApproverNode" class="header-btn"> |
| | | <el-icon><Plus /></el-icon> |
| | | 新增节点 |
| | | </el-button> |
| | | </div> |
| | | </template> |
| | | <div class="form-content"> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item> |
| | | <div class="approver-nodes-container"> |
| | | <div |
| | | v-for="(node, index) in approverNodes" |
| | | :key="node.id" |
| | | class="approver-node-item" |
| | | > |
| | | <div class="approver-node-label"> |
| | | <span class="node-step">{{ index + 1 }}</span> |
| | | <span class="node-text">审批人</span> |
| | | <el-icon class="arrow-icon"><ArrowRight /></el-icon> |
| | | </div> |
| | | <el-select |
| | | v-model="node.userId" |
| | | placeholder="选择人员" |
| | | class="approver-select" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" |
| | | /> |
| | | </el-select> |
| | | <el-button |
| | | type="danger" |
| | | size="small" |
| | | :icon="Delete" |
| | | @click="removeApproverNode(index)" |
| | | v-if="approverNodes.length > 1" |
| | | class="remove-btn" |
| | | >删除</el-button> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-row> --> |
| | | </div> |
| | | </el-card> |
| | | |
| | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted, markRaw, shallowRef } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete } from '@element-plus/icons-vue' |
| | | import { Search, Document, Box, EditPen, Plus } from '@element-plus/icons-vue' |
| | | import Pagination from '@/components/PIMTable/Pagination.vue' |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue' |
| | | import {getQuotationList,addQuotation,updateQuotation,deleteQuotation} from '@/api/salesManagement/salesQuotation.js' |
| | |
| | | const userList = ref([]); |
| | | const customerOption = ref([]); |
| | | |
| | | // 审批人节点相关 |
| | | const approverNodes = ref([ |
| | | { id: 1, userId: null } |
| | | ]) |
| | | let nextApproverId = 2 |
| | | |
| | | const isEdit = ref(false) |
| | | const editId = ref(null) |
| | | const currentQuotation = ref({}) |
| | | const formRef = ref() |
| | | |
| | | // 添加审批人节点 |
| | | function addApproverNode() { |
| | | approverNodes.value.push({ id: nextApproverId++, userId: null }) |
| | | } |
| | | |
| | | // 删除审批人节点 |
| | | function removeApproverNode(index) { |
| | | approverNodes.value.splice(index, 1) |
| | | } |
| | | |
| | | // 计算属性 |
| | | const filteredList = computed(() => { |
| | |
| | | }) |
| | | |
| | | // 方法 |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | '待审批': 'info', |
| | | '审核中': 'primary', |
| | | '通过': 'success', |
| | | '拒绝': 'danger' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | |
| | | const resetSearch = () => { |
| | | searchForm.quotationNo = '' |
| | | searchForm.customer = '' |
| | |
| | | dialogTitle.value = '新增报价' |
| | | isEdit.value = false |
| | | resetForm() |
| | | // 重置审批人节点 |
| | | approverNodes.value = [{ id: 1, userId: null }] |
| | | nextApproverId = 2 |
| | | dialogVisible.value = true |
| | | let userLists = await userListNoPage(); |
| | | // 只复制需要的字段,避免将组件引用放入响应式对象 |
| | |
| | | form.discountAmount = row.discountAmount || 0 |
| | | form.totalAmount = row.totalAmount || 0 |
| | | |
| | | // 反显审批人 |
| | | if (row.approveUserIds) { |
| | | const userIds = row.approveUserIds.split(',') |
| | | approverNodes.value = userIds.map((userId, idx) => ({ |
| | | id: idx + 1, |
| | | userId: parseInt(userId.trim()) |
| | | })) |
| | | nextApproverId = userIds.length + 1 |
| | | } else { |
| | | approverNodes.value = [{ id: 1, userId: null }] |
| | | nextApproverId = 2 |
| | | } |
| | | |
| | | // 加载用户列表 |
| | | let userLists = await userListNoPage(); |
| | | userList.value = (userLists.data || []).map(item => ({ |
| | |
| | | return |
| | | } |
| | | |
| | | // 审批人必填校验 |
| | | const hasEmptyApprover = approverNodes.value.some(node => !node.userId) |
| | | if (hasEmptyApprover) { |
| | | ElMessage.error('请为所有审批节点选择审批人!') |
| | | return |
| | | } |
| | | |
| | | // 收集所有节点的审批人id |
| | | form.approveUserIds = approverNodes.value.map(node => node.userId).join(',') |
| | | |
| | | // 计算所有产品的单价总和 |
| | | form.totalAmount = form.products.reduce((sum, product) => { |
| | | const price = Number(product.unitPrice) || 0 |
| | |
| | | validDate: item.validDate || '', |
| | | paymentMethod: item.paymentMethod || '', |
| | | status: item.status || '草稿', |
| | | // 审批人(用于编辑时反显) |
| | | approveUserIds: item.approveUserIds || '', |
| | | remark: item.remark || '', |
| | | products: item.products ? item.products.map(product => ({ |
| | | productId: product.productId || '', |
| | |
| | | } |
| | | } |
| | | |
| | | .approver-nodes-container { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 24px; |
| | | padding: 12px 0; |
| | | } |
| | | |
| | | .approver-node-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 12px; |
| | | padding: 16px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | border: 1px solid #e4e7ed; |
| | | transition: all 0.3s ease; |
| | | min-width: 180px; |
| | | |
| | | &:hover { |
| | | border-color: #409eff; |
| | | background: #f0f7ff; |
| | | box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1); |
| | | } |
| | | } |
| | | |
| | | .approver-node-label { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | |
| | | .node-step { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 24px; |
| | | height: 24px; |
| | | background: #409eff; |
| | | color: #fff; |
| | | border-radius: 50%; |
| | | font-size: 12px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .node-text { |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .arrow-icon { |
| | | color: #909399; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | |
| | | .approver-select { |
| | | width: 100%; |
| | | min-width: 150px; |
| | | } |
| | | |
| | | .remove-btn { |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .product-table { |
| | | :deep(.el-table__header) { |
| | | background-color: #f5f7fa; |
| | |
| | | text-align: right; |
| | | } |
| | | |
| | | // 响应式优化 |
| | | @media (max-width: 1200px) { |
| | | .approver-nodes-container { |
| | | gap: 16px; |
| | | } |
| | | |
| | | .approver-node-item { |
| | | min-width: 160px; |
| | | } |
| | | } |
| | | </style> |