You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

584 lines
21 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 生产bom树 -->
<el-col :lg="6" :xs="24" style="" v-loading="treeLoading">
<el-card shadow="hover">
<el-input v-model="materialName" placeholder="请输入物料名称" prefix-icon="Search" clearable/>
<el-tree
ref="materialBomTreeRef"
class="mt-2"
node-key="id"
:data="materialBomOptions"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
/>
</el-card>
</el-col>
<el-col :lg="18" :xs="24" v-loading="loading">
<div>
<el-divider content-position="left">物料</el-divider>
<el-form ref="parentMaterialBomFormRef" label-width="120px">
<el-row>
<el-form-item label="BOM版本" v-if="parentMaterialBomVersion">
{{ parentMaterialBomVersion }}
</el-form-item>
<el-form-item label="父物料类型名称" v-if="parentMaterialTypeName">
{{ parentMaterialTypeName }}
</el-form-item>
<el-form-item label="父物料名称">
{{ parentMaterialName }}
</el-form-item>
</el-row>
</el-form>
</div>
<el-divider content-position="left">子级物料</el-divider>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
:leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="子物料类型编号" prop="materialTypeCode" label-width="120px">
<el-input v-model="queryParams.materialTypeCode" placeholder="请输入物料类型编号" clearable
@keyup.enter="handleQuery"/>
</el-form-item>
<el-form-item label="子物料类型名称" prop="materialTypeName" label-width="120px">
<el-input v-model="queryParams.materialTypeName" placeholder="请输入物料类型名称" clearable
@keyup.enter="handleQuery"/>
</el-form-item>
<el-form-item label="子物料名称" prop="materialName" label-width="120px">
<el-input v-model="queryParams.materialName" placeholder="请输入物料名称" clearable
@keyup.enter="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10">
<el-col :span="1.5">
<el-button v-has-permi="['mes:materialBom:add']" type="success" plain icon="CircleCheck"
@click="submitBatchSaveMaterialBoms()" :loading="btnLoading">保存
</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-has-permi="['mes:materialBom:add']" type="primary" plain icon="Plus"
@click="handleAdd()" :loading="btnLoading">新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-has-permi="['mes:materialBom:remove']" type="danger" plain :disabled="multiple"
icon="Delete" @click="handleDelete()" :loading="btnLoading">
删除
</el-button>
</el-col>
</el-row>
</template>
<el-table :data="materialBomList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center"/>
<el-table-column type="index" width="50" align="center" label="序号"/>
<el-table-column v-if="columns[0].visible" key="materialBomId" label="生产BOM_ID" align="center"
prop="materialBomId" width="110px"/>
<el-table-column v-if="columns[1].visible" key="materialTypeCode" label="子物料类型编号" align="center"
prop="materialTypeCode" :show-overflow-tooltip="true" width="130px"/>
<el-table-column v-if="columns[2].visible" key="materialTypeName" label="子物料类型名称" align="center"
prop="materialTypeName" :show-overflow-tooltip="true" width="130px"/>
<el-table-column v-if="columns[3].visible" key="materialBomVersion" label="BOM版本" align="center"
prop="materialBomVersion" :show-overflow-tooltip="true" width="90px">
<template #default='scope'>
<el-input v-model='scope.row.materialBomVersion'/>
</template>
</el-table-column>
<el-table-column v-if="columns[4].visible" key="materialId" label="子物料ID" align="center"
prop="materialId" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[5].visible" key="materialName" label="子物料名称" align="center"
prop="materialName" :show-overflow-tooltip="true" width="300px">
<template #default='scope'>
<el-input v-model='scope.row.materialName' placeholder='请点击检索物料'
@click='handleMaterialSelect(scope.row)' readonly/>
</template>
</el-table-column>
<el-table-column v-if="columns[6].visible" key="standardAmount" label="子物料数量" align="center"
prop="standardAmount" :show-overflow-tooltip="true" width="100px">
<template #default='scope'>
<el-input v-model='scope.row.standardAmount'/>
</template>
</el-table-column>
<el-table-column v-if="columns[7].visible" key="unitName" label="子物料单位" align="center"
prop="unitName" :show-overflow-tooltip="true" width="100px"/>
</el-table>
<!-- <pagination-->
<!-- v-show="total > 0"-->
<!-- v-model:page="queryParams.pageNum"-->
<!-- v-model:limit="queryParams.pageSize"-->
<!-- :total="total"-->
<!-- @pagination="getList"-->
<!-- />-->
</el-card>
</el-col>
</el-row>
<!-- 添加或修改生产bom配置对话框 -->
<el-dialog ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body
@close="closeDialog">
<el-form ref="materialBomFormRef" :model="form" :rules="rules" label-width="98px">
<el-form-item label="子物料类型" prop='materialTypeId'>
<el-select v-model="form.materialTypeId" placeholder="请选择" style="width:360px">
<el-option
v-for="item in materialTypeOptions"
:key="item.materialTypeId"
:label="item.materialTypeName"
:value="item.materialTypeId"
></el-option>
</el-select>
</el-form-item>
<el-form-item label='BOM版本' prop='materialBomVersion' v-if="parentMaterialId===0">
<el-input v-model='form.materialBomVersion' placeholder='请输入BOM版本' style="width:360px"/>
</el-form-item>
<el-form-item label='子物料ID' prop='materialId' v-if="false">
<el-input v-model='form.materialId' placeholder='' style="width:360px"/>
</el-form-item>
<el-form-item label='子物料名称' prop='materialName'>
<el-input v-model='form.materialName' placeholder='请点击检索物料' @click='handleMaterialSelect(form)'
style="width:360px"/>
</el-form-item>
<el-form-item label='子物料数量' prop='standardAmount'>
<el-input-number v-model='form.standardAmount' :precision="2" :step="0.1" placeholder='请输入子物料数量'
style="width:360px"/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm" :loading="submitLoading"> </el-button>
<el-button @click="cancel()"> </el-button>
</div>
</template>
</el-dialog>
<!-- 添加物料信息对话框 -->
<el-dialog title="选择物料信息" v-model='materialOpen' append-to-body>
<MaterialSelect @selection="handleSelection" ref="materialSelectRef"
:materialTypeId="materialTypeId"
v-if="materialOpen"></MaterialSelect>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitMaterialForm"> </el-button>
<el-button @click="materialOpen = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script setup name="ProdMaterialBom" lang="ts">
import {
materialBomTreeSelect,
listMaterialBomJoinStructure,
addBatchMaterialBom, updateMaterialBom, addMaterialBom, delMaterialBom
} from "@/api/mes/materialBom";
import {MaterialBomVO, MaterialBomForm, MaterialBomQuery} from '@/api/mes/materialBom/types';
import {BaseMaterialTypeVO} from '@/api/mes/baseMaterialType/types';
import MaterialSelect from '@/views/mes/baseMaterialInfo/addMaterial.vue';
import {to} from 'await-to-js';
import {nextTick} from "vue";
const materialTypeId = ref(0);
const router = useRouter();
const {proxy} = getCurrentInstance() as ComponentInternalInstance;
const materialBomList = ref<MaterialBomVO[]>();
const saveMaterialBomList = ref<MaterialBomVO[]>([]);
const treeLoading = ref(false);
const loading = ref(false);
const materialSelectRef = ref();
const materialOpen = ref(false);
const editedRow = ref();
const submitLoading = ref(false);
const btnLoading = ref(false);
const showSearch = ref(true);
const selectedMaterialBoms = ref<MaterialBomVO[]>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const parentFormDisplay = ref(false);
const childTableTitle = ref('');
const materialName = ref('');
const materialBomOptions = ref<MaterialBomVO[]>([]);
const materialTypeOptions = ref<BaseMaterialTypeVO[]>([]);
const parentMaterialId = ref(1);
const parentParentMaterialId = ref();
const parentMaterialTypeName = ref('');
const parentMaterialName = ref('');
const parentMaterialBomVersion = ref('');
// 列显隐信息
const columns = ref<FieldOption[]>([
{key: 0, label: `生产BOM_ID`, visible: false, children: []},
{key: 1, label: `子物料类型编号`, visible: true, children: []},
{key: 2, label: `子物料类型名称`, visible: true, children: []},
{key: 3, label: `BOM版本`, visible: false, children: []},
{key: 4, label: `子物料ID`, visible: false, children: []},
{key: 5, label: `子物料名称`, visible: true, children: []},
{key: 6, label: `子物料数量`, visible: true, children: []},
{key: 7, label: `子物料单位`, visible: true, children: []}
]);
const materialBomTreeRef = ref<ElTreeInstance>();
const queryFormRef = ref<ElFormInstance>();
const materialBomFormRef = ref<ElFormInstance>();
const formDialogRef = ref<ElDialogInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: MaterialBomForm = {
materialBomId: undefined,
parentId: undefined,
materialId: undefined,
materialName: undefined,
materialTypeId: undefined,
materialBomDesc: undefined,
materialBomVersion: undefined,
ancestors: undefined,
standardAmount: undefined,
topFlag: undefined,
checkType: '1',
projectId: undefined,
assembleTime: undefined,
materialClassfication: undefined,
attachId: undefined,
activeFlag: '1',
remark: undefined,
materialTypeName: undefined,
};
const data = reactive<PageData<MaterialBomForm, MaterialBomQuery>>({
form: {...initFormData},
queryParams: {
materialBomId: undefined,
parentId: undefined,
materialTypeCode: undefined,
materialTypeName: undefined,
materialName: undefined,
materialBomVersion: undefined,
bomStructureParentId: undefined,
params: {}
},
rules: {
materialTypeId: [
{required: true, message: "物料类型不能为空", trigger: "blur"}
],
materialBomVersion: [
{required: true, message: "BOM版本不能为空", trigger: "blur"}
],
materialName: [
{required: true, message: "物料名称不能为空", trigger: "blur"}
],
standardAmount: [
{required: true, message: "子物料数量不能为空", trigger: "blur"}
]
}
});
const {queryParams, form, rules} = toRefs(data);
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.label?.toLowerCase().includes(value.toLowerCase());
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
materialBomTreeRef.value?.filter(materialName.value);
},
{
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
}
);
/** 查询物料类型bom结构树下拉树结构 */
const getTreeSelect = async () => {
treeLoading.value = true;
loading.value = true;
const res = await materialBomTreeSelect();
// alert(JSON.stringify(res))
materialBomOptions.value = res.data;
//此处的parentid是materialtypeid
columns.value[3].visible = false;
if (queryParams.value.parentId === null || queryParams.value.parentId === undefined) {
const topMaterialBom = materialBomOptions.value.find(item => item.id === 0);
queryParams.value.bomStructureParentId = 1;//赋值结构bom的parentId
queryParams.value.parentId = 0;
parentMaterialId.value = 0;
parentParentMaterialId.value = undefined;
parentMaterialName.value = topMaterialBom.materialName;
// parentParentMaterialTypeName.value = "顶级";
}
if (queryParams.value.parentId === 0) {
columns.value[3].visible = true;
}
nextTick(function () {
materialBomTreeRef.value?.setCurrentKey(queryParams.value.parentId, true);
handleQuery();
})
// const dd = {"id":"1","parentId":-1,"label":"dd"};
// materialTypeOptions.value[0].children.push(dd);
};
/** 查询生产bom列表 */
const getList = async () => {
loading.value = true;
const res = await listMaterialBomJoinStructure(queryParams.value);
materialBomList.value = res.data;
loading.value = false;
treeLoading.value = false;
// total.value = res.total;
};
/** 节点单击事件 */
const handleNodeClick = (data: MaterialBomVO) => {
queryParams.value.bomStructureParentId = data.materialTypeId;
queryParams.value.parentId = data.id;
parentMaterialId.value = data.id;
parentParentMaterialId.value = data.parentId;
parentMaterialName.value = data.materialName;
parentMaterialTypeName.value = data.materialTypeName;
parentMaterialBomVersion.value = data.materialBomVersion;
columns.value[3].visible = false;
if (data.id === 0) {
columns.value[3].visible = true;
}
handleQuery();
};
const clickDisplay = () => {
if (queryParams.value.parentId === 0) {
parentFormDisplay.value = false;
childTableTitle.value = "顶级物料";
} else {
parentFormDisplay.value = true;
childTableTitle.value = "子级物料";
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
// queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
// dateRange.value = ['', ''];
queryFormRef.value?.resetFields();
// queryParams.value.pageNum = 1;
// queryParams.value.parentId = undefined;
// structureBomTreeRef.value?.setCurrentKey(undefined);
handleQuery();
};
/** 删除按钮操作 */
const handleDelete = async (row?: MaterialBomVO) => {
btnLoading.value = true;
const materialBomIds = [];
selectedMaterialBoms.value.forEach(item => {
if (item.materialBomId && item.materialBomId !== '') {
materialBomIds.push(item.materialBomId);
} else {
deleteReset(item);
}
});
if (materialBomIds.length > 0) {
try {
const [err] = await to(proxy?.$modal.confirm('是否确认删除生产BOM编号为"' + materialBomIds + '"的数据项?') as any);
if (!err) {
await delMaterialBom(materialBomIds)
await getTreeSelect();
proxy?.$modal.msgSuccess('删除成功');
}
} catch {
btnLoading.value = false;
}
}
btnLoading.value = false;
};
const deleteReset = (item) => {
item.materialId = undefined;
item.materialName = undefined;
item.materialBomVersion = undefined;
item.standardAmount = undefined;
};
/** 选择条数 */
const handleSelectionChange = (selection: MaterialBomVO[]) => {
selectedMaterialBoms.value = selection.map((item) => item);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 重置操作表单 */
const reset = () => {
form.value = {...initFormData};
materialBomFormRef.value?.resetFields();
};
/** 取消按钮 */
const cancel = () => {
dialog.visible = false;
reset();
};
/** 新增按钮操作 */
const handleAdd = async () => {
reset();
materialTypeOptions.value = Array.from(
new Map(materialBomList.value.map(item => [item['materialTypeId'], item]))
).map(([_, item]) => item);
dialog.visible = true;
dialog.title = '新增';
form.value.parentId = queryParams.value.parentId;
};
/** 提交按钮 */
const submitForm = () => {
materialBomFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
submitLoading.value = true;
try {
form.value.materialBomId ? await updateMaterialBom(form.value) : await addMaterialBom(form.value);
proxy?.$modal.msgSuccess('操作成功');
await getTreeSelect();
dialog.visible = false;
submitLoading.value = false;
} catch {
submitLoading.value = false;
}
// const child = {"id":"1","parentId":-1,"label":"dd"};
// alert(materialTypeOptions.value[0])
// materialTypeOptions.value[0].children.push(dd);
}
});
};
/**
* 关闭用户弹窗
*/
const closeDialog = () => {
dialog.visible = false;
resetForm();
};
/**
* 重置表单
*/
const resetForm = () => {
materialBomFormRef.value?.resetFields();
materialBomFormRef.value?.clearValidate();
form.value.materialBomId = undefined;
form.value.activeFlag = '1';
};
onMounted(() => {
getTreeSelect(); // 初始化物料类型结构bom数据
// initMaterialTypeData();
});
const handleSelection = () => {
// this.ids = selection.map(item => item.materialBomId)
// this.single = selection.length !== 1
// this.multiple = !selection.length
};
const handleMaterialSelect = (row) => {
editedRow.value = row;
materialTypeId.value = row.materialTypeId;
materialOpen.value = true;
};
/** 提交物料信息按钮 */
const submitMaterialForm = () => {
let selectedRow = materialSelectRef.value.tableRef.store.states.currentRow.value;
editedRow.value.materialId = selectedRow.materialId;
editedRow.value.materialName = selectedRow.materialName;
materialOpen.value = false;
};
const submitBatchSaveMaterialBoms = async () => {
try {
btnLoading.value = true;
saveMaterialBomList.value = [];
for (let i = 0; i < materialBomList.value.length; i++) {
let item = materialBomList.value[i];
if (item.materialId) {
if (!item.materialBomVersion && parentMaterialId.value === 0) {
proxy?.$modal.msgWarning('序号为[' + parseInt(i) + 1 + ']请填写BOM版本');
btnLoading.value = false;
return;
}
if (!item.standardAmount) {
proxy?.$modal.msgWarning('序号为[' + parseInt(i) + 1 + '],请填写子物料数量');
btnLoading.value = false;
return;
}
item.parentId = parentMaterialId.value;
saveMaterialBomList.value.push(item);
}
}
if (saveMaterialBomList.value.length <= 0) {
proxy?.$modal.msgWarning('无可保存子级物料');
btnLoading.value = false;
return;
}
await addBatchMaterialBom(saveMaterialBomList.value);
proxy?.$modal.msgSuccess('操作成功');
await getTreeSelect();
btnLoading.value = false;
} catch (e) {
alert(e)
btnLoading.value = false;
}
}
</script>
<style lang="scss" scoped></style>