| | |
| | | </template> |
| | | <div class="form-content"> |
| | | <el-table :data="form.products" border style="width: 100%" class="product-table" v-if="form.products.length > 0"> |
| | | <el-table-column prop="product" label="产品名称" width="200"> |
| | | <el-table-column prop="product" label="产品名称" width="220"> |
| | | <template #default="scope"> |
| | | <el-form-item :prop="`products.${scope.$index}.productId`" class="product-table-form-item"> |
| | | <el-tree-select |
| | | v-model="scope.row.productId" |
| | | placeholder="请选择" |
| | | clearable |
| | | check-strictly |
| | | @change="getModels($event, scope.row)" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | <span>{{ scope.row.product || scope.row.productName || '--' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="specification" label="图纸编号" width="200"> |
| | | <el-table-column prop="specification" label="图纸编号" width="220"> |
| | | <template #default="scope"> |
| | | <el-form-item :prop="`products.${scope.$index}.specificationId`" class="product-table-form-item"> |
| | | <el-select |
| | | v-model="scope.row.specificationId" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="getProductModel($event, scope.row)" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in scope.row.modelOptions || []" |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <span>{{ scope.row.specification || '--' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="unit" label="单位"> |
| | |
| | | <el-table-column prop="unitPrice" label="单价"> |
| | | <template #default="scope"> |
| | | <el-form-item :prop="`products.${scope.$index}.unitPrice`" class="product-table-form-item"> |
| | | <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" /> |
| | | <el-input-number v-model="scope.row.unitPrice" :min="0.01" :precision="2" :step="0.01" style="width: 100%" /> |
| | | </el-form-item> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | </el-form> |
| | | </div> |
| | | </FormDialog> |
| | | |
| | | <ProductSelectDialog |
| | | v-model="productSelectVisible" |
| | | :single="true" |
| | | @confirm="handleProductSelectConfirm" |
| | | /> |
| | | |
| | | <!-- 查看详情对话框 --> |
| | | <el-dialog v-model="viewDialogVisible" title="报价详情" width="800px"> |
| | |
| | | import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete } from '@element-plus/icons-vue' |
| | | import Pagination from '@/components/PIMTable/Pagination.vue' |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue' |
| | | import ProductSelectDialog from '@/views/basicData/product/ProductSelectDialog.vue' |
| | | import {getQuotationList,addQuotation,updateQuotation,deleteQuotation} from '@/api/salesManagement/salesQuotation.js' |
| | | import {userListNoPage} from "@/api/system/user.js"; |
| | | import {customerList} from "@/api/salesManagement/salesLedger.js"; |
| | |
| | | |
| | | const quotationList = ref([]) |
| | | const productOptions = ref([]); |
| | | const modelOptions = ref([]); |
| | | const pagination = reactive({ |
| | | total: 3, |
| | | currentPage: 1, |
| | |
| | | |
| | | const dialogVisible = ref(false) |
| | | const viewDialogVisible = ref(false) |
| | | const productSelectVisible = ref(false) |
| | | const activeProductIndex = ref(-1) |
| | | const dialogTitle = ref('新增报价') |
| | | const form = reactive({ |
| | | quotationNo: '', |
| | |
| | | } |
| | | return null; |
| | | } |
| | | const getModels = (value, row) => { |
| | | if (!row) return; |
| | | // 如果清空选择,则清空相关字段 |
| | | if (!value) { |
| | | row.productId = ''; |
| | | row.product = ''; |
| | | row.modelOptions = []; |
| | | row.specificationId = ''; |
| | | row.specification = ''; |
| | | row.unit = ''; |
| | | return; |
| | | } |
| | | // 更新 productId(v-model 已经自动更新,这里确保一致性) |
| | | row.productId = value; |
| | | // 找到对应的 label 并赋值给 row.product |
| | | const label = findNodeById(productOptions.value, value); |
| | | if (label) { |
| | | row.product = label; |
| | | } |
| | | // 获取规格型号列表,设置到当前行的 modelOptions |
| | | modelList({ id: value }).then((res) => { |
| | | row.modelOptions = res || []; |
| | | }); |
| | | }; |
| | | const getProductModel = (value, row) => { |
| | | if (!row) return; |
| | | // 如果清空选择,则清空相关字段 |
| | | if (!value) { |
| | | row.specificationId = ''; |
| | | row.specification = ''; |
| | | row.unit = ''; |
| | | return; |
| | | } |
| | | // 更新 specificationId(v-model 已经自动更新,这里确保一致性) |
| | | row.specificationId = value; |
| | | const modelOptions = row.modelOptions || []; |
| | | const index = modelOptions.findIndex((item) => item.id === value); |
| | | if (index !== -1) { |
| | | row.specification = modelOptions[index].model; |
| | | row.unit = modelOptions[index].unit; |
| | | } else { |
| | | row.specification = ''; |
| | | row.unit = ''; |
| | | } |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // 找到节点,返回 label |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundLabel = findNodeById(nodes[i].children, productId); |
| | | if (foundLabel) { |
| | | return foundLabel; // 在子节点中找到,返回 label |
| | | } |
| | | } |
| | | } |
| | | return null; // 没有找到节点,返回null |
| | | }; |
| | | const handleView = (row) => { |
| | | // 只复制需要的字段,避免将组件引用放入响应式对象 |
| | | currentQuotation.value = { |
| | |
| | | } |
| | | |
| | | const addProduct = () => { |
| | | form.products.push({ |
| | | productId: '', |
| | | product: '', |
| | | productName: '', |
| | | specificationId: '', |
| | | specification: '', |
| | | quantity: 1, |
| | | unit: '', |
| | | unitPrice: 0, |
| | | amount: 0, |
| | | modelOptions: [] // 为每行添加独立的规格型号列表 |
| | | }) |
| | | activeProductIndex.value = -1 |
| | | productSelectVisible.value = true |
| | | } |
| | | |
| | | const removeProduct = (index) => { |
| | | form.products.splice(index, 1) |
| | | calculateSubtotal() |
| | | } |
| | | |
| | | const handleProductSelectConfirm = (rows) => { |
| | | if (!rows || rows.length === 0 || activeProductIndex.value < 0) { |
| | | if (!rows || rows.length === 0) { |
| | | return |
| | | } |
| | | const selected = rows[0] |
| | | form.products.push({ |
| | | productId: selected.id, |
| | | product: selected.productName || '', |
| | | productName: selected.productName || '', |
| | | specificationId: selected.id, |
| | | specification: selected.model || '', |
| | | quantity: 1, |
| | | unit: selected.unit || '', |
| | | unitPrice: 0.01, |
| | | amount: 0, |
| | | modelOptions: [], |
| | | }) |
| | | calculateSubtotal() |
| | | activeProductIndex.value = -1 |
| | | return |
| | | } |
| | | |
| | | const row = form.products[activeProductIndex.value] |
| | | if (!row) { |
| | | return |
| | | } |
| | | |
| | | const selected = rows[0] |
| | | row.productId = selected.id |
| | | row.product = selected.productName || '' |
| | | row.productName = selected.productName || '' |
| | | row.specificationId = selected.id |
| | | row.specification = selected.model || '' |
| | | row.unit = selected.unit || row.unit || '' |
| | | calculateAmount(row) |
| | | } |
| | | |
| | | const calculateAmount = (product) => { |
| | |
| | | return |
| | | } |
| | | |
| | | // 审批人必填校验 |
| | | const hasEmptyApprover = approverNodes.value.some(node => !node.userId) |
| | | if (hasEmptyApprover) { |
| | | ElMessage.error('请为所有审批节点选择审批人!') |
| | | const hasInvalidPrice = form.products.some(product => Number(product.unitPrice) <= 0) |
| | | if (hasInvalidPrice) { |
| | | ElMessage.error('单价必须大于0') |
| | | return |
| | | } |
| | | |
| | | // 收集所有节点的审批人id |
| | | form.approveUserIds = approverNodes.value.map(node => node.userId).join(',') |
| | | |
| | | // 收集所有节点的审批人id,允许为空 |
| | | form.approveUserIds = approverNodes.value |
| | | .map(node => node.userId) |
| | | .filter(userId => userId !== null && userId !== undefined && userId !== '') |
| | | .join(',') |
| | | |
| | | // 计算所有产品的单价总和 |
| | | form.totalAmount = form.products.reduce((sum, product) => { |