src/api/inventoryManagement/stockInventory.js
@@ -25,3 +25,13 @@ data: params, }); }; export const exportStockInventory = (params) => { return request({ url: "/stockInventory/exportStockInventory", method: "post", data: params, }); }; src/api/productionManagement/productBom.js
@@ -45,3 +45,13 @@ params: { productModelId }, }); } // 导出BOM export function exportBom(bomId) { return request({ url: "/productBom/exportBom", method: "post", params: { bomId }, responseType: "blob", }); } src/views/basicData/product/ProductSelectDialog.vue
@@ -1,28 +1,12 @@ <template> <el-dialog v-model="visible" title="选择产品" width="900px" destroy-on-close :close-on-click-modal="false" > <el-dialog v-model="visible" title="选择产品" width="900px" destroy-on-close :close-on-click-modal="false"> <el-form :inline="true" :model="query" class="mb-2"> <el-form-item label="产品大类"> <el-input v-model="query.productName" placeholder="输入产品大类" clearable @keyup.enter="onSearch" /> <el-input v-model="query.productName" placeholder="输入产品大类" clearable @keyup.enter="onSearch" /> </el-form-item> <el-form-item label="型号名称"> <el-input v-model="query.model" placeholder="输入型号名称" clearable @keyup.enter="onSearch" /> <el-input v-model="query.model" placeholder="输入型号名称" clearable @keyup.enter="onSearch" /> </el-form-item> <el-form-item> @@ -32,32 +16,19 @@ </el-form> <!-- 列表 --> <el-table v-loading="loading" :data="tableData" height="420" highlight-current-row row-key="id" @selection-change="handleSelectionChange" > <el-table ref="tableRef" v-loading="loading" :data="tableData" height="420" highlight-current-row row-key="id" @selection-change="handleSelectionChange" @select="handleSelect"> <el-table-column type="selection" width="55" /> <el-table-column type="index" label="#" width="60"/> <el-table-column type="index" label="序号" width="60" /> <el-table-column prop="productName" label="产品大类" min-width="160"/> <el-table-column prop="model" label="型号名称" min-width="200"/> <el-table-column prop="unit" label="单位" min-width="160"/> </el-table> <div class="mt-3 flex justify-end"> <el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total" v-model:page-size="page.pageSize" v-model:current-page="page.pageNum" :page-sizes="[10, 20, 50, 100]" @size-change="onPageChange" @current-change="onPageChange" /> <el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total" v-model:page-size="page.pageSize" v-model:current-page="page.pageNum" :page-sizes="[10, 20, 50, 100]" @size-change="onPageChange" @current-change="onPageChange" /> </div> <template #footer> @@ -70,7 +41,7 @@ </template> <script setup lang="ts"> import {computed, onMounted, reactive, ref, watch} from "vue"; import { computed, onMounted, reactive, ref, watch, nextTick } from "vue"; import {ElMessage} from "element-plus"; import {productModelList} from '@/api/basicData/productModel' @@ -83,6 +54,7 @@ const props = defineProps<{ modelValue: boolean; single?: boolean; // 是否只能选择一个,默认false(可选择多个) }>(); const emit = defineEmits(['update:modelValue', 'confirm']); @@ -105,14 +77,48 @@ const loading = ref(false); const tableData = ref<ProductRow[]>([]); const total = ref(0); const multipleSelection = ref<ProductRow[]>([]) const multipleSelection = ref<ProductRow[]>([]); const tableRef = ref(); function close() { visible.value = false; } const handleSelectionChange = (val: ProductRow[]) => { multipleSelection.value = val if (props.single && val.length > 1) { // 如果限制为单个选择,只保留最后一个选中的 const lastSelected = val[val.length - 1]; multipleSelection.value = [lastSelected]; // 清空表格选中状态,然后重新选中最后一个 nextTick(() => { if (tableRef.value) { tableRef.value.clearSelection(); tableRef.value.toggleRowSelection(lastSelected, true); } }); } else { multipleSelection.value = val; } } // 处理单个选择 const handleSelect = (selection: ProductRow[], row: ProductRow) => { if (props.single) { // 如果限制为单个,清空其他选择,只保留当前行 if (selection.includes(row)) { // 选中当前行时,清空其他选中 multipleSelection.value = [row]; nextTick(() => { if (tableRef.value) { tableData.value.forEach((item) => { if (item.id !== row.id) { tableRef.value.toggleRowSelection(item, false); } }); } }); } } } function onSearch() { @@ -136,7 +142,11 @@ ElMessage.warning("请选择一条产品"); return; } emit("confirm", multipleSelection.value); if (props.single && multipleSelection.value.length > 1) { ElMessage.warning("只能选择一个产品"); return; } emit("confirm", props.single ? [multipleSelection.value[0]] : multipleSelection.value); close(); } @@ -144,7 +154,7 @@ loading.value = true; try { multipleSelection.value = []; // 翻页/搜索后清空选择更符合预期 const res = await productModelList({ const res: any = await productModelList({ productName: query.productName.trim(), model: query.model.trim(), current: page.pageNum, @@ -157,6 +167,13 @@ } } // 监听弹窗打开,重置选择 watch(() => props.modelValue, (visible) => { if (visible) { multipleSelection.value = []; } }); onMounted(() => { loadData() }) src/views/collaborativeApproval/sealManagement/index.vue
@@ -27,7 +27,7 @@ <el-option label="已拒绝" value="rejected" /> </el-select> </el-col> <el-col :span="8"> <el-col :span="6"> <el-button type="primary" @click="searchSealApplications">搜索</el-button> <el-button @click="resetSealSearch">重置</el-button> <el-button @click="handleExport">导出</el-button> src/views/equipmentManagement/measurementEquipment/components/formDia.vue
@@ -27,7 +27,7 @@ </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="安装位置:" prop="installationLocation"> <el-form-item label="安装位置:" prop="instationLocation"> <el-input v-model="form.installationLocation" placeholder="请输入" src/views/equipmentManagement/measurementEquipment/index.vue
@@ -82,14 +82,8 @@ align:"center" }, { label: "部门", prop: "deptName", width: 130, align:"center" }, { label: "安装位置", prop: "installationLocation", prop: "instationLocation", width: 150, align:"center" }, src/views/inventoryManagement/dispatchLog/Record.vue
@@ -214,7 +214,7 @@ type: "warning", }) .then(() => { proxy.download("/stockmanagement/export", {}, "出库台账.xlsx"); proxy.download("/stockOutRecord/exportStockOutRecord", {type: props.type}, props.type === '0' ? "合格出库台账.xlsx" : "不合格出库台账.xlsx"); }) .catch(() => { proxy.$modal.msg("已取消"); src/views/inventoryManagement/receiptManagement/Record.vue
@@ -203,8 +203,7 @@ }) .then(() => { // 根据不同的 tab 类型调用不同的导出接口 let exportUrl = "/stockin/export"; proxy.download(exportUrl, {}, "入库台账.xlsx"); proxy.download("/stockInRecord/exportStockInRecord", {type: props.type}, props.type === '0' ? "合格入库.xlsx" : "不合格入库.xlsx"); }) .catch(() => { proxy.$modal.msg("已取消"); src/views/inventoryManagement/stockManagement/Qualified.vue
@@ -135,7 +135,7 @@ type: 'warning', } ).then(() => { proxy.download("/stockin/exportCopy", {}, '库存信息.xlsx') proxy.download("/stockInventory/exportStockInventory", {}, '合格库存信息.xlsx') }).catch(() => { proxy.$modal.msg("已取消") }) src/views/inventoryManagement/stockManagement/Unqualified.vue
@@ -135,7 +135,7 @@ type: 'warning', } ).then(() => { proxy.download("/stockin/exportCopy", {}, '库存信息.xlsx') proxy.download("/stockUninventory/exportStockUninventory", {}, '不合格库存信息.xlsx') }).catch(() => { proxy.$modal.msg("已取消") }) src/views/productionManagement/productStructure/Detail/index.vue
@@ -2,133 +2,85 @@ <div class="app-container"> <PageHeader content="产品结构详情"> <template #right-button> <el-button v-if="dataValue.isEdit && !isOrderPage" type="primary" @click="addItem">添加 <el-button v-if="!dataValue.isEdit && !isOrderPage" type="primary" @click="dataValue.isEdit = true">编辑 </el-button> <el-button v-if="!dataValue.isEdit && !isOrderPage" type="primary" @click="dataValue.isEdit = true">编辑 <el-button v-if="dataValue.isEdit && !isOrderPage" type="primary" @click="cancelEdit">取消 </el-button> <el-button v-if="dataValue.isEdit && !isOrderPage" type="primary" @click="cancelEdit">取消 </el-button> <el-button v-if="!isOrderPage" type="primary" :loading="dataValue.loading" @click="submit" <el-button v-if="!isOrderPage" type="primary" :loading="dataValue.loading" @click="submit" :disabled="!dataValue.isEdit">确认 </el-button> </template> </PageHeader> <el-table :data="tableData" border :preserve-expanded-content="false" :default-expand-all="true" style="width: 100%" > <el-table :data="tableData" border :preserve-expanded-content="false" :default-expand-all="true" style="width: 100%"> <el-table-column type="expand"> <template #default="props"> <el-form ref="form" :model="dataValue"> <el-table :data="dataValue.dataList" style="width: 100%"> <el-table-column prop="productName" label="产品"/> <el-table-column prop="model" label="规格"> <el-form ref="form" :model="dataValue"> <el-table :data="dataValue.dataList" row-key="tempId" default-expand-all :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" style="width: 100%"> <el-table-column prop="productName" label="产品" /> <el-table-column prop="model" label="规格"> <template #default="{ row, $index }"> <el-form-item v-if="dataValue.isEdit" :prop="`dataList.${$index}.model`" :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]" style="margin: 0"> <el-select v-model="row.model" placeholder="请选择规格" clearable :disabled="!dataValue.isEdit" style="width: 100%" @visible-change="(v) => { if (v) openDialog($index) }"> <el-option v-if="row.model" :label="row.model" :value="row.model" /> :rules="[{ required: true, message: '请选择规格', trigger: ['blur', 'change'] }]" style="margin: 0"> <el-select v-model="row.model" placeholder="请选择规格" clearable :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" style="width: 100%" @visible-change="(v) => { if (v) openDialog(row.tempId) }"> <el-option v-if="row.model" :label="row.model" :value="row.model" /> </el-select> </el-form-item> </template> </el-table-column> <el-table-column prop="processId" label="消耗工序"> <el-table-column prop="processName" label="消耗工序"> <template #default="{ row, $index }"> <el-form-item :prop="`dataList.${$index}.processId`" :rules="[{ required: true, message: '请选择消耗工序', trigger: 'change' }]" <el-form-item v-if="dataValue.isEdit" :rules="dataValue.dataList.some(item => (item as any).tempId === row.tempId) ? [] : [{ required: true, message: '请选择消耗工序', trigger: 'change' }]" style="margin: 0"> <el-select v-model="row.processId" placeholder="请选择" filterable clearable style="width: 100%" :disabled="!dataValue.isEdit"> <el-option v-for="item in dataValue.processOptions" :key="item.id" :label="item.name" <el-select v-model="row.processId" placeholder="请选择" filterable clearable style="width: 100%" :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)"> <el-option v-for="item in dataValue.processOptions" :key="item.id" :label="item.name" :value="item.id" /> </el-select> </el-form-item> </template> </el-table-column> <el-table-column prop="unitQuantity" label="单位产出所需数量"> <el-table-column prop="unitQuantity" label="单位产出所需数量"> <template #default="{ row, $index }"> <el-form-item :prop="`dataList.${$index}.unitQuantity`" <el-form-item v-if="dataValue.isEdit" :rules="[{ required: true, message: '请输入单位产出所需数量', trigger: ['blur','change'] }]" style="margin: 0"> <el-input-number v-model="row.unitQuantity" :min="0" :precision="2" :step="1" controls-position="right" style="width: 100%" :disabled="!dataValue.isEdit" /> <el-input-number v-model="row.unitQuantity" :min="0" :precision="2" :step="1" controls-position="right" style="width: 100%" :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" /> </el-form-item> </template> </el-table-column> <el-table-column v-if="isOrderPage" prop="demandedQuantity" label="需求总量"> <el-table-column v-if="isOrderPage" prop="demandedQuantity" label="需求总量"> <template #default="{ row, $index }"> <el-form-item :prop="`dataList.${$index}.demandedQuantity`" :rules="[{ required: true, message: '请输入需求总量', trigger: ['blur','change'] }]" style="margin: 0"> <el-input-number v-model="row.demandedQuantity" :min="0" :precision="2" :step="1" controls-position="right" style="width: 100%" :disabled="!dataValue.isEdit" /> <el-form-item v-if="dataValue.isEdit" :rules="[{ required: true, message: '请输入需求总量', trigger: ['blur', 'change'] }]" style="margin: 0"> <el-input-number v-model="row.demandedQuantity" :min="0" :precision="2" :step="1" controls-position="right" style="width: 100%" :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" /> </el-form-item> </template> </el-table-column> <el-table-column prop="unit" label="单位"> <el-table-column prop="unit" label="单位"> <template #default="{ row, $index }"> <el-form-item :prop="`dataList.${$index}.unit`" :rules="[{ required: true, message: '请输入单位', trigger: ['blur','change'] }]" style="margin: 0"> <el-input v-model="row.unit" placeholder="请输入单位" clearable :disabled="!dataValue.isEdit" /> <el-form-item v-if="dataValue.isEdit" :rules="[{ required: true, message: '请输入单位', trigger: ['blur', 'change'] }]" style="margin: 0"> <el-input v-model="row.unit" placeholder="请输入单位" clearable :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" /> </el-form-item> </template> </el-table-column> <el-table-column label="操作" fixed="right" width="100"> <el-table-column label="操作" fixed="right" width="200"> <template #default="{ row, $index }"> <el-button v-if="dataValue.isEdit" type="danger" text @click="dataValue.dataList.splice($index, 1)">删除 <el-button v-if="dataValue.isEdit && !dataValue.dataList.some(item => (item as any).tempId === row.tempId)" type="danger" text @click="removeItem(row.tempId)">删除 </el-button> <el-button v-if="dataValue.isEdit" type="primary" text @click="addItem2(row.tempId)">添加 </el-button> </template> </el-table-column> @@ -140,9 +92,7 @@ <el-table-column label="产品名称" prop="productName" /> <el-table-column label="规格型号" prop="model" /> </el-table> <product-select-dialog v-if="dataValue.showProductDialog" v-model:model-value="dataValue.showProductDialog" <product-select-dialog v-if="dataValue.showProductDialog" v-model:model-value="dataValue.showProductDialog" @confirm="handleProduct" /> </div> </template> @@ -169,27 +119,32 @@ const ProductSelectDialog = defineAsyncComponent( () => import("@/views/basicData/product/ProductSelectDialog.vue") ); const emit = defineEmits(["update:router"]); const form = ref(); const route = useRoute() const router = useRouter() const route = useRoute(); const router = useRouter(); const routeId = computed({ get() { return route.query.id; }, set(val) { emit('update:router', val) } emit("update:router", val); }, }); // 从路由参数获取产品信息 const routeBomNo = computed(() => route.query.bomNo || ''); const routeProductName = computed(() => route.query.productName || ''); const routeProductModelName = computed(() => route.query.productModelName || ''); const routeBomNo = computed(() => route.query.bomNo || ""); const routeProductName = computed(() => route.query.productName || ""); const routeProductModelName = computed( () => route.query.productModelName || "" ); const routeOrderId = computed(() => route.query.orderId); const pageType = computed(() => route.query.type); const isOrderPage = computed(() => pageType.value === 'order' && routeOrderId.value); const isOrderPage = computed( () => pageType.value === "order" && routeOrderId.value ); const dataValue = reactive({ dataList: [], @@ -197,6 +152,7 @@ processOptions: [], showProductDialog: false, currentRowIndex: null, currentRowName: null, loading: false, isEdit: false, }); @@ -206,11 +162,12 @@ productName: "", model: "", bomNo: "", } ]) }, ]); const openDialog = index => { dataValue.currentRowIndex = index; const openDialog = (tempId: any) => { console.log(tempId, "tempId"); dataValue.currentRowName = tempId; dataValue.showProductDialog = true; }; @@ -218,55 +175,233 @@ if (isOrderPage.value) { // 订单情况:使用订单的产品结构接口 const { data } = await listProcessBom({ orderId: routeOrderId.value }); dataValue.dataList = data || []; dataValue.dataList = (data as any) || []; } else { // 非订单情况:使用原来的接口 const { data } = await queryList(routeId.value); dataValue.dataList = data || []; dataValue.dataList = (data as any) || []; // 为所有项及其子项设置name属性 const setNameRecursively = (items: any[]) => { items.forEach((item: any) => { item.tempId = item.id; item.processName = dataValue.processOptions.find(option => option.id === item.processId) ?.name || ""; if (item.children && item.children.length > 0) { setNameRecursively(item.children); } }); }; setNameRecursively(dataValue.dataList); console.log(dataValue.dataList, "dataValue.dataList"); } }; const fetchProcessOptions = async () => { const { data } = await list(routeId.value); dataValue.processOptions = data; const { data } = await list(); dataValue.processOptions = data as any; }; const handleProduct = row => { const handleProduct = (row: any) => { if (row?.length > 1) { ElMessage.error("只能选择一个产品"); } dataValue.dataList[dataValue.currentRowIndex].productName = row[0].productName; dataValue.dataList[dataValue.currentRowIndex].model = row[0].model; dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id; dataValue.dataList[dataValue.currentRowIndex].unit = row[0].unit || ""; const productData = row[0]; // 最外层组件中,与当前产品相同的产品只能有一个 const isTopLevel = dataValue.dataList.some(item => (item as any).tempId === dataValue.currentRowName); if (isTopLevel) { if (productData.productName === tableData[0].productName && productData.model === tableData[0].model) { // 查找是否已经有其他顶层行已经是这个产品 const hasOther = dataValue.dataList.some(item => (item as any).tempId !== dataValue.currentRowName && (item as any).productName === tableData[0].productName && (item as any).model === tableData[0].model ); if (hasOther) { ElMessage.warning("最外层和当前产品一样的一级只能有一个"); return; } } } // dataValue.dataList[dataValue.currentRowIndex].productName = // row[0].productName; // dataValue.dataList[dataValue.currentRowIndex].model = row[0].model; // dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id; // dataValue.dataList[dataValue.currentRowIndex].unit = row[0].unit || ""; dataValue.dataList.map(item => { if (item.tempId === dataValue.currentRowName) { item.productName = productData.productName; item.model = productData.model; item.productModelId = productData.id; item.unit = productData.unit || ""; return; } childItem(item, dataValue.currentRowName, productData); }); dataValue.showProductDialog = false; }; const childItem = (item: any, tempId: any, productData: any) => { if (item.tempId === tempId) { item.productName = productData.productName; item.model = productData.model; item.productModelId = productData.id; item.unit = productData.unit || ""; return true; } if (item.children && item.children.length > 0) { for (let child of item.children) { if (childItem(child, tempId, productData)) { return true; } } } return false; }; // 递归校验所有层级的表单数据 const validateAll = () => { let isValid = true; // 校验函数 const validateItem = (item: any, isTopLevel = false) => { // 校验当前项的必填字段 if (!item.model) { ElMessage.error("请选择规格"); isValid = false; return; } if (!isTopLevel && !item.processId) { ElMessage.error("请选择消耗工序"); isValid = false; return; } if (!item.unitQuantity) { ElMessage.error("请输入单位产出所需数量"); isValid = false; return; } if (isOrderPage.value && !item.demandedQuantity) { ElMessage.error("请输入需求总量"); isValid = false; return; } if (!item.unit) { ElMessage.error("请输入单位"); isValid = false; return; } // 递归校验子项 if (item.children && item.children.length > 0) { item.children.forEach(child => { validateItem(child, false); }); } }; // 遍历所有顶层项 dataValue.dataList.forEach(item => { validateItem(item, true); }); return isValid; }; const submit = () => { form.value .validate(valid => { dataValue.loading = true; // 先进行表单校验 const valid = validateAll(); console.log(dataValue.dataList, "dataValue.dataList"); if (valid) { add({ bomId: routeId.value, productStructureList: dataValue.dataList || [], }).then(res => { router.push({ path: '/productionManagement/productionManagement/productStructure/index', children: dataValue.dataList || [], }) .then(res => { router.push({ path: "/productionManagement/productionManagement/productStructure/index", }); ElMessage.success("保存成功"); dataValue.loading = false; }); } }) .finally(() => { .catch(() => { dataValue.loading = false; }); } else { dataValue.loading = false; } }; const addItem = () => { dataValue.dataList.push({ const removeItem = (tempId: string) => { // 先尝试从顶层删除 const topIndex = dataValue.dataList.findIndex(item => item.tempId === tempId); if (topIndex !== -1) { dataValue.dataList.splice(topIndex, 1); return; } // 递归删除子项 const delchildItem = (items: any[], tempId: any) => { for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.tempId === tempId) { items.splice(i, 1); return true; } if (item.children && item.children.length > 0) { if (delchildItem(item.children, tempId)) { return true; } } } return false; }; dataValue.dataList.forEach(item => { if (item.children && item.children.length > 0) { delchildItem(item.children, tempId); } }); }; const addItem2 = tempId => { dataValue.dataList.map(item => { if (item.tempId === tempId) { if (!item.children) { item.children = []; } item.children.push({ parentId: item.id || "", parentTempId: item.tempId || "", productName: "", productId: "", model: undefined, productModelId: undefined, processId: "", processName: "", unitQuantity: 0, demandedQuantity: 0, unit: "", children: [], tempId: new Date().getTime(), }); return; } addchildItem(item, tempId); }); }; const addchildItem = (item: any, tempId: any) => { if (item.tempId === tempId) { console.log(item, "item"); if (!item.children) { item.children = []; } item.children.push({ parentId: item.id || "", parentTempId: item.tempId || "", productName: "", productId: "", model: undefined, @@ -274,27 +409,41 @@ processId: "", unitQuantity: 0, demandedQuantity: 0, children: [], unit: "", tempId: new Date().getTime(), }); return true; } if (item.children && item.children.length > 0) { for (let child of item.children) { if (addchildItem(child, tempId)) { return true; } } } return false; }; const cancelEdit = () => { dataValue.isEdit = false; dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined); // dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined); fetchData(); }; onMounted(() => { onMounted(async () => { // 从路由参数回显数据 tableData[0].productName = routeProductName.value; tableData[0].model = routeProductModelName.value; tableData[0].bomNo = routeBomNo.value; tableData[0].productName = routeProductName.value as string; tableData[0].model = routeProductModelName.value as string; tableData[0].bomNo = routeBomNo.value as string; // 订单情况下禁用编辑 if (isOrderPage.value) { dataValue.isEdit = false; } fetchData(); fetchProcessOptions(); // 先加载工序选项,再加载数据,确保el-select能够正确回显 await fetchProcessOptions(); await fetchData(); }); </script> src/views/productionManagement/productStructure/index.vue
@@ -1,42 +1,25 @@ <template> <div class="app-container"> <div style="text-align: right; margin-bottom: 10px;"> <el-button type="info" plain icon="Upload" @click="handleImport">导入</el-button> <el-button type="warning" plain icon="Download" @click="handleExport" :disabled="selectedRows.length !== 1">导出</el-button> <el-button type="primary" @click="handleAdd">新增</el-button> <el-button type="danger" plain @click="handleBatchDelete" :disabled="selectedRows.length === 0">删除</el-button> </div> <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true" @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination" > <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true" @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination"> <template #detail="{row}"> <el-button type="primary" text @click="showDetail(row)">{{ row.bomNo }} <el-button type="primary" text @click="showDetail(row)">{{ row.bomNo }} </el-button> </template> </PIMTable> <StructureEdit v-if="showEdit" v-model:show-model="showEdit" :record="currentRow"/> <!-- 新增/编辑弹窗 --> <el-dialog v-model="dialogVisible" :title="operationType === 'add' ? '新增BOM' : '编辑BOM'" width="600px" @close="closeDialog" > <el-form ref="formRef" :model="form" :rules="rules" label-width="120px" > <el-dialog v-model="dialogVisible" :title="operationType === 'add' ? '新增BOM' : '编辑BOM'" width="600px" @close="closeDialog"> <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> <el-form-item label="产品名称" prop="productModelId"> <el-button type="primary" @click="showProductSelectDialog = true"> {{ form.productName || '选择产品' }} @@ -46,13 +29,7 @@ <el-input v-model="form.version" placeholder="请输入版本号" clearable /> </el-form-item> <el-form-item label="备注" prop="remark"> <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" clearable /> <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" clearable /> </el-form-item> </el-form> <template #footer> @@ -62,17 +39,35 @@ </el-dialog> <!-- 产品选择弹窗 --> <ProductSelectDialog v-model="showProductSelectDialog" @confirm="handleProductSelect" single /> <ProductSelectDialog v-model="showProductSelectDialog" @confirm="handleProductSelect" single /> <!-- BOM导入对话框 --> <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body> <el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag> <el-icon class="el-icon--upload"><upload-filled /></el-icon> <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> <template #tip> <div class="el-upload__tip text-center"> <span>仅允许导入xls、xlsx格式文件。</span> </div> </template> </el-upload> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitFileForm">确 定</el-button> <el-button @click="upload.open = false">取 消</el-button> </div> </template> </el-dialog> </div> </template> <script setup> import { ref, reactive, toRefs, onMounted, getCurrentInstance, defineAsyncComponent } from "vue"; import { listPage, add, update, batchDelete } from "@/api/productionManagement/productBom.js"; import { getToken } from "@/utils/auth"; import { listPage, add, update, batchDelete, exportBom } from "@/api/productionManagement/productBom.js"; import { useRouter } from 'vue-router' import { ElMessageBox } from 'element-plus' import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; @@ -145,6 +140,20 @@ const operationType = ref('add'); // add | edit const formRef = ref(null); const showProductSelectDialog = ref(false); // BOM导入参数 const upload = reactive({ // 是否显示弹出层(BOM导入) open: false, // 弹出层标题(BOM导入) title: "", // 是否禁用上传 isUploading: false, // 设置上传的请求头部 headers: { Authorization: "Bearer " + getToken() }, // 上传的地址 url: import.meta.env.VITE_APP_BASE_API + "/productBom/uploadBom" }); const page = reactive({ current: 1, @@ -321,6 +330,73 @@ formRef.value?.resetFields(); }; // 导入按钮操作 const handleImport = () => { upload.title = "BOM导入"; upload.open = true; }; // 文件上传中处理 const handleFileUploadProgress = (event, file, fileList) => { upload.isUploading = true; }; // 文件上传成功处理 const handleFileSuccess = (response, file, fileList) => { upload.open = false; upload.isUploading = false; proxy.$refs["uploadRef"].handleRemove(file); if (response.code === 200) { proxy.$modal.msgSuccess(response.msg || "导入成功"); getList(); } else { proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true }); } }; // 提交上传文件 const submitFileForm = () => { proxy.$refs["uploadRef"].submit(); }; // 导出按钮操作 const handleExport = () => { if (selectedRows.value.length !== 1) { proxy.$modal.msgWarning("请选择一条数据进行导出"); return; } const bomId = selectedRows.value[0].id; const fileName = `BOM_${selectedRows.value[0].bomNo || bomId}.xlsx`; exportBom(bomId).then(res => { // 返回的数据是否为空 if (!res) { proxy.$modal.msgError("导出失败,返回数据为空"); return; } const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); const downloadElement = document.createElement('a'); const href = window.URL.createObjectURL(blob); downloadElement.style.display = 'none'; downloadElement.href = href; downloadElement.download = fileName; document.body.appendChild(downloadElement); downloadElement.click(); document.body.removeChild(downloadElement); window.URL.revokeObjectURL(href); proxy.$modal.msgSuccess("导出成功"); }).catch(err => { console.error("导出异常:", err); proxy.$modal.msgError("系统异常,导出失败"); }); }; // 查看详情 const showDetail = (row) => { router.push({ src/views/salesManagement/salesLedger/index.vue
@@ -6,8 +6,16 @@ <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="客户合同号:"> <el-input v-model="searchForm.customerContractNo" placeholder="请输入" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="销售合同号:"> <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="项目名称:"> <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="录入日期:"> @@ -26,7 +34,6 @@ <el-button type="primary" @click="openForm('add')"> 新增台账 </el-button> <el-button @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> @@ -34,7 +41,7 @@ </div> <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%" :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 21em)"> :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)"> <el-table-column align="center" type="selection" width="55" /> <el-table-column type="expand"> <template #default="props"> @@ -43,80 +50,72 @@ <el-table-column label="产品大类" prop="productCategory" /> <el-table-column label="规格型号" prop="specificationModel" /> <el-table-column label="单位" prop="unit" /> <el-table-column label="产品状态" width="100px" align="center"> <template #default="scope"> <el-tag v-if="scope.row.approveStatus === 0" type="info">未出库</el-tag> <el-tag v-if="scope.row.approveStatus === 1" type="success">已出库</el-tag> <el-tag v-if="scope.row.approveStatus === 2" type="warning">审核中</el-tag> <el-tag v-if="scope.row.approveStatus === 3" type="success">审核成功</el-tag> <el-tag v-if="scope.row.approveStatus === 4" type="danger">审核失败</el-tag> </template> </el-table-column> <el-table-column label="发货车牌" minWidth="100px" align="center"> <template #default="scope"> <div> <el-tag type="success" v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag> <el-tag v-else type="info">未发货</el-tag> </div> </template> </el-table-column> <el-table-column label="发货日期" minWidth="100px" align="center"> <template #default="scope"> <div> <div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div> <el-tag v-else type="info">未发货</el-tag> </div> </template> </el-table-column> <el-table-column label="数量" prop="quantity" /> <el-table-column label="税率(%)" prop="taxRate" /> <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> <el-table-column label="产品状态" width="100px" align="center"> <!--操作--> <el-table-column Width="60px" label="操作" align="center"> <template #default="scope"> <el-tag v-if="scope.row.approveStatus === 1" type="success">充足</el-tag> <el-tag v-else type="danger">不足</el-tag> </template> </el-table-column> <el-table-column label="发货状态" prop="shippingStatus" width="140" align="center" show-overflow-tooltip /> <el-table-column label="发货日期" minWidth="100px" align="center"> <template #default="scope"> <div> <div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div> <el-tag v-else type="info">-</el-tag> </div> </template> </el-table-column> <el-table-column Width="60px" label="操作" align="center"> <template #default="scope"> <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button> <el-button :disabled="scope.row.approveStatus!==2 || scope.row.approveStatus!==5" link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button> </template> </el-table-column> </el-table> </template> </el-table-column> <el-table-column align="center" label="序号" type="index" width="60" /> <el-table-column label="销售合同号" prop="salesContractNo" show-overflow-tooltip /> <el-table-column label="客户名称" prop="customerName" show-overflow-tooltip /> <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip /> <el-table-column label="客户合同号" prop="customerContractNo" 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="contractAmount" width="220" show-overflow-tooltip :formatter="formattedNumber" /> <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip /> <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip /> <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip /> <el-table-column fixed="right" label="操作" width="120" align="center"> <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip /> <el-table-column fixed="right" label="操作" min-width="100" align="center"> <template #default="scope"> <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button> <!-- <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详情</el-button>--> <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button> <!-- <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>--> </template> </el-table-column> </el-table> <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" :page="page.current" :limit="page.size" @pagination="paginationChange" /> </div> <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" :width="'70%'" :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia"> <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" width="70%" @close="closeDia"> <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> <el-row v-if="operationType !== 'view'"> <el-col :span="24" style="display:flex; justify-content:flex-end; gap:10px; margin-bottom: 6px;"> <el-button type="primary" plain @click="openQuotationDialog">从审批通过的报价单导入</el-button> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="销售合同号:" prop="salesContractNo"> @@ -125,9 +124,7 @@ </el-col> <el-col :span="12"> <el-form-item label="业务员:" prop="salesman"> <el-select v-model="form.salesman" filterable :reserve-keyword="false" placeholder="请选择" clearable :disabled="operationType === 'view'"> <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'"> <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName" /> </el-select> @@ -136,8 +133,13 @@ </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="客户合同号:" prop="customerContractNo"> <el-input v-model="form.customerContractNo" placeholder="请输入" clearable :disabled="operationType === 'view'"/> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="客户名称:" prop="customerId"> <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable> <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'"> <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id"> {{ item.customerName + "——" + item.taxpayerIdentificationNumber @@ -146,6 +148,13 @@ </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <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-col :span="12"> <el-form-item label="签订日期:" prop="executionDate"> <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD"