<script setup>
|
import { reactive, ref, computed, watch,onMounted } from "vue";
|
import {useI18n} from "vue-i18n";
|
import request from "@/utils/request"
|
import {changeFilterEvent, filterChanged} from "@/hook";
|
import deepClone from "@/utils/deepClone"
|
import { ElMessage, ElMessageBox} from "element-plus"
|
const { t } = useI18n()
|
|
let produceList = ref([])
|
let props = defineProps({
|
productName:null,
|
produceId:null,
|
baseGridOptions: { type: Object, required: true },
|
|
gridDataByProduct: { type: Object, default: () => ({}) }
|
})
|
const xGrid = ref()
|
const xGridByProduct = ref()
|
const activeProduct = ref('') // 当前选中的产品名
|
let materialType = ref(1)
|
|
const value = ref('')
|
const options = [
|
{
|
value: t('ingredients.originalFilm'),
|
label: t('ingredients.originalFilm')
|
},
|
{
|
value: t('ingredients.accessories'),
|
label: t('ingredients.accessories'),
|
}
|
]
|
const gridOptions = reactive({
|
border: "full",//表格加边框
|
keepSource: true,//保持源数据
|
align: 'center',//文字居中
|
stripe:true,//斑马纹
|
rowConfig: {isCurrent: true, isHover: true,height: 30, useKey: true},//鼠标移动或选择高亮
|
id: 'productBOM',
|
showFooter: true,//显示脚
|
printConfig: {},
|
importConfig: {},
|
exportConfig: {},
|
scrollX:{enabled: true},
|
scrollY:{ enabled: true ,gt:0},//开启虚拟滚动
|
showOverflow:true,
|
columnConfig: {
|
resizable: true,
|
useKey: true
|
},
|
customConfig: {
|
storage: true
|
},
|
columns:[
|
],//表头参数
|
data:[
|
],//表格数据
|
toolbarConfig: {
|
slots:{
|
buttons: "toolbar_buttons"
|
}
|
}
|
|
})
|
const gridOptionsByProduct = reactive({
|
border: "full",//表格加边框
|
keepSource: true,//保持源数据
|
align: 'center',//文字居中
|
stripe:true,//斑马纹
|
rowConfig: {isCurrent: true, isHover: true,height: 30, useKey: true},//鼠标移动或选择高亮
|
id: 'productBOMByProduct',
|
showFooter: true,//显示脚
|
printConfig: {},
|
importConfig: {},
|
exportConfig: {},
|
scrollX:{enabled: true},
|
scrollY:{ enabled: true ,gt:0},//开启虚拟滚动
|
showOverflow:true,
|
columnConfig: {
|
resizable: true,
|
useKey: true
|
},
|
customConfig: {
|
storage: true
|
},
|
editConfig: {
|
trigger: 'click',
|
mode: 'row',
|
showStatus: true
|
},
|
columns:[
|
{title: '', width: '110', slots: { default: 'button_slot' },fixed:'left'},
|
{field:'id',title: t('ingredients.materialCode') },
|
{field: 'name', title: t('ingredientsStock.materialName')},
|
{field: 'thickness', title: t('product.msg.allThickness')},
|
{field: 'width', title: t('order.width')},
|
{field: 'height', title: t('order.height')},
|
{field:'consume',title: t('bom.consume') },
|
{field:'unit',title: t('ingredients.unit') },
|
{field:'price',title: t('bom.price1') }
|
],//表头参数
|
data:[
|
|
],//表格数据
|
toolbarConfig: {
|
slots: {
|
tools: 'toolbar_buttons'
|
}
|
}
|
|
})
|
let arr = [
|
{title: t('basicData.add'), width: '110', slots: { default: 'button_slot' },fixed:'left'},
|
{field: 'tabId', width: '150',title: 'BOMId', sortable: true,showOverflow:'ellipsis' ,filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged},
|
{field: 'id', width: '150',title: t('ingredients.materialCode'), sortable: true,showOverflow:'ellipsis' ,filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged},
|
{field: 'consume',title: t('bom.consume'),showOverflow:'ellipsis' },
|
{field: 'price',title: t('bom.price'),showOverflow:'ellipsis' }
|
]
|
const bomTitle = [
|
|
]
|
let pageNum=ref(1)
|
let total = reactive({
|
pageTotal : 0,
|
dataTotal : 0,
|
pageSize : 1000
|
})
|
let filterData = ref({
|
type:''
|
|
})
|
let BasicData = ref([])
|
|
let materialStore= ref([])
|
onMounted(async () => {
|
//第一次加载默认
|
value.value=t('ingredients.originalFilm')
|
filterData.value.type=t('ingredients.originalFilm')
|
request.get(`/BasicWarehouse/BasicWarehouseType/${value.value}`).then((res) => {
|
if(res.code==200){
|
gridOptions.columns.splice(0,gridOptions.columns.length)
|
BasicData.value = res.data
|
//添加列
|
gridOptions.columns=arr.slice()
|
for (let i=0;i<BasicData.value.length;i++){
|
let aa={field: BasicData.value[i].OperateType, width: '150',title: BasicData.value[i].OperateTypeName, sortable: true,showOverflow:'ellipsis' ,filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}
|
gridOptions.columns.push(aa)
|
}
|
bomTitle.forEach((item) => {
|
gridOptions.columns.push(item)
|
})
|
|
getWorks()
|
|
}else{
|
ElMessage.warning(res.msg)
|
}
|
})
|
})
|
|
|
|
//列查询
|
const getWork = () => {
|
filterData.value.type=value.value
|
request.get(`/BasicWarehouse/BasicWarehouseType/${value.value}`).then((res) => {
|
if(res.code==200){
|
gridOptions.columns=[]
|
BasicData.value = res.data
|
//添加列
|
gridOptions.columns=arr.slice()
|
|
for (let i=0;i<BasicData.value.length;i++){
|
let column={field: BasicData.value[i].OperateType,
|
width: '150',title: BasicData.value[i].OperateTypeName,
|
sortable: true,showOverflow:'ellipsis' ,
|
filters:[{ data: '' }],
|
slots: { filter: 'num1_filter' },
|
filterMethod:filterChanged}
|
|
gridOptions.columns.push(column)
|
|
}
|
bomTitle.forEach((item) => {
|
gridOptions.columns.push(item)
|
})
|
getWorks()
|
|
}else{
|
ElMessage.warning(res.msg)
|
}
|
})
|
}
|
|
|
//数据绑定
|
const getWorks = () => {
|
request.post(`/materialStore/getSelectProductBOM/1/${total.pageSize}`,filterData.value).then((res) => {
|
|
if(res.code==200){
|
materialStore.value=[]
|
for (let i=0;i<res.data.data.length;i++){
|
materialStore.value[i]= JSON.parse(res.data.data[i].json)
|
materialStore.value[i].id= res.data.data[i].id
|
materialStore.value[i].consume= res.data.data[i].consume
|
materialStore.value[i].tabId= res.data.data[i].tabId
|
switch (res.data.data[i].bomType) {
|
case '1':
|
materialStore.value[i].bomType = t('order.area');
|
break;
|
case '2':
|
materialStore.value[i].bomType = t('order.perimeter');
|
break;
|
case "3":
|
materialStore.value[i].bomType = t('order.quantity');
|
break;
|
default:
|
materialStore.value[i].bomType = res.data.data[i].bomType; // 保留原值
|
}
|
materialStore.value[i].price= res.data.data[i].price
|
}
|
|
total.dataTotal = res.data.total.total*1
|
total.pageTotal= res.data.total.pageTotal
|
pageNum.value=1
|
|
produceList = deepClone(materialStore.value)
|
xGrid.value.loadData(produceList)
|
gridOptions.loading=false
|
}else{
|
ElMessage.warning(res.msg)
|
router.push("/login")
|
}
|
})
|
}
|
|
|
//拆分每个产品
|
const productList = computed(() => {
|
const raw = (props.productName ?? '').toString().trim()
|
if (!raw) return [t('bom.other')]
|
|
const parts = raw
|
.split(/[+*]/)
|
.map(s => s.trim())
|
.filter(Boolean)
|
|
parts.push(t('bom.other'))
|
|
return parts
|
})
|
|
// 当前选中 tab 的下标
|
const activeProductIndex = ref(null)
|
|
const gridDataMapByProduct = reactive({})
|
|
const handleTabClick = (name, index) => {
|
activeProductIndex.value = index
|
if (!gridDataMapByProduct[index]) {
|
gridDataMapByProduct[index] = [] // 初始化
|
}
|
xGridByProduct.value?.loadData(gridDataMapByProduct[index])
|
}
|
const getTableRow = (row, type) => {
|
if (type !== 'add') return
|
const key = activeProductIndex.value
|
if (key=="" && key==null) {
|
ElMessage.warning(t('bom.msg.msg1'))
|
return
|
}
|
const plainRow = JSON.parse(JSON.stringify(row))
|
const oldData = (gridDataMapByProduct[key] || []).map((item) =>
|
JSON.parse(JSON.stringify(item))
|
)
|
gridDataMapByProduct[key] = [...oldData, plainRow]
|
xGridByProduct.value.loadData(gridDataMapByProduct[key])
|
}
|
const getTableRowDils = (row,type) =>{
|
switch (type) {
|
case 'delete' :{
|
const key = activeProductIndex.value
|
gridDataMapByProduct[key] = (gridDataMapByProduct[key] || []).filter(
|
item => item.id !== row.id
|
)
|
|
xGridByProduct.value.loadData(gridDataMapByProduct[key])
|
return
|
}
|
}
|
}
|
|
const saveProductBOM = () => {
|
productList.value.forEach((name, index) => {
|
const rows = gridDataMapByProduct[index] || []
|
const seq = index + 1
|
|
//给每条数据加上对应层号、产品编号
|
rows.forEach(row => {
|
row.layer = seq
|
row.produceId = props.produceId
|
})
|
})
|
console.log(gridDataMapByProduct)
|
request.post(`/BomData/saveProductBOM`,gridDataMapByProduct).then((res) => {
|
if(res.code==200){
|
ElMessage.success(t('basicData.msg.saveSuccess'))
|
}})
|
}
|
</script>
|
|
<template>
|
<div id="main_add">
|
<div class="product">
|
<el-card class="product-card" shadow="never">
|
<div class="product-tabs">
|
<p
|
v-for="(name, i) in productList"
|
:key="i"
|
class="text item tab"
|
:class="{ active: activeProductIndex === i }"
|
@click="handleTabClick(name, i)"
|
>
|
{{ name }}
|
</p>
|
</div>
|
</el-card>
|
</div>
|
<div class="productLayer">
|
<vxe-grid
|
class="mytable-scrollbar"
|
ref="xGridByProduct"
|
height="100%"
|
size="small"
|
v-bind="gridOptionsByProduct"
|
|
>
|
<template #toolbar_buttons>
|
<el-button type="primary" @click="saveProductBOM">保存</el-button>
|
</template>
|
<template #button_slot="{ row }">
|
<el-button @click="getTableRowDils(row,'delete')"
|
|
link type="primary" size="small">{{ $t('basicData.delete') }}</el-button>
|
|
</template>
|
</vxe-grid>
|
|
</div>
|
<div class="MaterialBOM">
|
<vxe-grid
|
class="mytable-scrollbar"
|
ref="xGrid"
|
height="100%"
|
v-bind="gridOptions"
|
size="small"
|
>
|
<template #toolbar_buttons>
|
<el-select v-model="value" style="width: 150px;" :placeholder="$t('ingredients.pleaseSelectACategory')" @change="getWork">
|
<el-option
|
v-for="item in options"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
</template>
|
<template #num1_filter="{ column, $panel }">
|
<div>
|
<div v-for="(option, index) in column.filters" :key="index">
|
<input type="type" v-model="option.data" @keyup.enter.native="$panel.confirmFilter()" @input="changeFilterEvent($event, option, $panel)"/>
|
</div>
|
</div>
|
</template>
|
<template #button_slot="{ row }">
|
<el-button @click="getTableRow(row,'add')"
|
|
link type="primary" size="small">{{ $t('basicData.add') }}</el-button>
|
|
</template>
|
</vxe-grid>
|
</div>
|
</div>
|
|
|
|
</template>
|
|
<style scoped>
|
#main_add{
|
width: 100%;
|
height: 100%;
|
}
|
.product{
|
width: 30%;
|
height:50%;
|
float: left;
|
}
|
.productLayer{
|
width: 65%;
|
height:50%;
|
float: left;
|
}
|
.MaterialBOM{
|
width: 100%;
|
height:50%;
|
float: left;
|
}
|
.product-card { max-width: 960px; padding: 2px 2px 4px; }
|
.product-tabs { display: flex; flex-direction: column; gap: 2px 12px; }
|
.tab {
|
margin: 0; padding: 6px 10px; line-height: 1.6;
|
border-radius: 6px; cursor: pointer; user-select: none;
|
transition: all .15s ease; border: 1px solid var(--el-border-color);
|
}
|
.tab:hover { transform: translateY(-1px); }
|
.tab.active {
|
background: var(--el-color-primary-light-9);
|
border-color: var(--el-color-primary);
|
color: var(--el-color-primary); font-weight: 600;
|
}
|
|
.tab.active {
|
font-weight: bold;
|
color: #409EFF; /* Element Plus 主色 */
|
border-bottom: 2px solid #409EFF;
|
}
|
</style>
|