refactor(组件): 重构产品与原材料信息组件,使用直接传递对象替代ID映射

将ProductInfo和RawMaterialInfo组件从基于ID映射数据改为直接接收product/material对象
移除对vuex state的依赖和相关的计算属性
创建缓存版本组件ProductInfoCache和RawMaterialInfoCache
更新所有使用这些组件的视图文件以传递完整对象
This commit is contained in:
砂糖
2025-11-15 16:04:41 +08:00
parent 9e02caecf2
commit ef3a764b19
15 changed files with 291 additions and 85 deletions

View File

@@ -64,12 +64,12 @@
min-width="250" min-width="250"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType === 'product'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType === 'product'" :product="scope.row.product">
<template #default="{ product }"> <template #default="{ product }">
{{ product.productName || '未知' }}({{ product.productCode || '无编码' }}) {{ product.productName || '未知' }}({{ product.productCode || '无编码' }})
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }"> <template #default="{ material }">
{{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }}) {{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }})
</template> </template>

View File

@@ -47,12 +47,12 @@
</el-table-column> </el-table-column>
<el-table-column label="产品类型" align="center" min-width="250"> <el-table-column label="产品类型" align="center" min-width="250">
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType === 'product'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType === 'product'" :product="scope.row.product">
<template #default="{ product }"> <template #default="{ product }">
{{ product.productName || '未知' }}({{ product.productCode || '无编码' }}) {{ product.productName || '未知' }}({{ product.productCode || '无编码' }})
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }"> <template #default="{ material }">
{{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }}) {{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }})
</template> </template>

View File

@@ -81,12 +81,12 @@
<el-table-column label="物料类型" align="center" prop="materialType"/> <el-table-column label="物料类型" align="center" prop="materialType"/>
<el-table-column label="产品类型" align="center" prop="itemName"> <el-table-column label="产品类型" align="center" prop="itemName">
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row.product">
<template slot-scope="{ product }"> <template slot-scope="{ product }">
{{ product.productName }}({{ product.productCode }}) {{ product.productName }}({{ product.productCode }})
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template slot-scope="{ material }"> <template slot-scope="{ material }">
{{ material.rawMaterialName }}({{ material.rawMaterialCode }}) {{ material.rawMaterialName }}({{ material.rawMaterialCode }})
</template> </template>

View File

@@ -6,7 +6,7 @@
<el-button v-if="canAdd" @click="add" icon="el-icon-plus">未搜索到产品点击添加</el-button> <el-button v-if="canAdd" @click="add" icon="el-icon-plus">未搜索到产品点击添加</el-button>
<div v-else style="padding: 10px;">未搜索到产品</div> <div v-else style="padding: 10px;">未搜索到产品</div>
</template> </template>
<el-option v-for="item in productOptions" :key="item.productId" <el-option v-for="item in productList" :key="item.productId"
:label="getLabel(item)" :value="item.productId"> :label="getLabel(item)" :value="item.productId">
<div> <div>
<div class="option-label"> <div class="option-label">
@@ -55,14 +55,15 @@
<el-button @click="cancel"> </el-button> <el-button @click="cancel"> </el-button>
</div> </div>
<BomPanel v-if="activeStep === 1" :id="bomId" :itemId="itemId" :type="addForm.type" @addBom="handleBom" /> <!-- <BomPanel v-if="activeStep === 1" :id="bomId" :itemId="itemId" :type="addForm.type" @addBom="handleBom" /> -->
</el-dialog> </el-dialog>
</span> </span>
</template> </template>
<script> <script>
import { listProductWithBom, addProduct } from '@/api/wms/product'; // import { listProductWithBom, addProduct } from '@/api/wms/product';
import BomPanel from '../BomPanel/index.vue'; // import BomPanel from '../BomPanel/index.vue';
import { mapGetters } from 'vuex';
export default { export default {
name: 'ProductSelect', name: 'ProductSelect',
@@ -82,12 +83,12 @@ export default {
type: Boolean type: Boolean
} }
}, },
components: { // components: {
BomPanel // BomPanel
}, // },
data() { data() {
return { return {
productOptions: [], // productOptions: [],
selected: this.value, selected: this.value,
addForm: { addForm: {
productCode: undefined, productCode: undefined,
@@ -136,15 +137,18 @@ export default {
} }
} }
}, },
computed: {
...mapGetters(['productList']),
},
created() { created() {
this.getProductOptions(); // this.getProductOptions();
}, },
methods: { methods: {
getProductOptions() { // getProductOptions() {
listProductWithBom({ pageNum: 1, pageSize: 1000 }).then(res => { // listProductWithBom({ pageNum: 1, pageSize: 1000 }).then(res => {
this.productOptions = res.rows || []; // this.productOptions = res.rows || [];
}); // });
}, // },
getLabel(item) { getLabel(item) {
// 产品名称[规格](参数),如果有则写,没有则省略 // 产品名称[规格](参数),如果有则写,没有则省略
const sku = this.getSku(item); const sku = this.getSku(item);

View File

@@ -47,47 +47,47 @@
</template> </template>
<script> <script>
import { mapState } from 'vuex'; // import { mapState } from 'vuex';
import BomInfo from './BomInfo.vue'; // import BomInfo from './BomInfo.vue';
export default { export default {
name: 'ProductInfo', name: 'ProductInfo',
components: { // components: {
BomInfo // BomInfo
}, // },
props: { props: {
productId: { product: {
type: [String, Number], type: Object,
required: true required: true
}, },
}, },
mounted() { // mounted() {
console.log(this.productId, this.productMap); // console.log(this.productId, this.productMap);
}, // },
data() { data() {
return { return {
showDetail: false, showDetail: false,
// product: {}, // product: {},
}; };
}, },
computed: { // computed: {
...mapState({ // ...mapState({
productMap: state => state.category.productMap // productMap: state => state.category.productMap
}), // }),
product() { // product() {
// 检查 productMap 是否已加载 // // 检查 productMap 是否已加载
if (!this.productMap || Object.keys(this.productMap).length === 0) { // if (!this.productMap || Object.keys(this.productMap).length === 0) {
return {}; // return {};
} // }
if (!this.productId) { // if (!this.productId) {
return {}; // return {};
} // }
return this.productMap[this.productId.toString()] || {}; // return this.productMap[this.productId.toString()] || {};
}, // },
loading() { // loading() {
return !this.productMap || Object.keys(this.productMap).length === 0 // return !this.productMap || Object.keys(this.productMap).length === 0
} // }
}, // },
methods: { methods: {
clickHandle() { clickHandle() {
this.showDetail = true; this.showDetail = true;

View File

@@ -0,0 +1,119 @@
<template>
<div v-loading="loading" loading-text="加载中...">
<span class="product-name" @click.stop="clickHandle">
<slot name="default" :product="product">
{{ product && product.productName ? product.productName : '--' }}
</slot>
</span>
<el-dialog
:visible="showDetail"
@close="showDetail = false"
:title="product && product.productName ? product.productName : '--' "
width="500px"
append-to-body
>
<el-descriptions :column="1" border>
<el-descriptions-item label="产品名称">
{{ product.productName || '--' }}
</el-descriptions-item>
<el-descriptions-item label="产品编码">
{{ product.productCode || '--' }}
</el-descriptions-item>
<el-descriptions-item label="规格">
{{ product.specification || '--' }}
</el-descriptions-item>
<el-descriptions-item label="材质">
{{ product.material || '--' }}
</el-descriptions-item>
<el-descriptions-item label="表面处理">
{{ product.surfaceTreatment || '--' }}
</el-descriptions-item>
<!-- 锌层 -->
<el-descriptions-item label="锌层">
{{ product.zincLayer || '--' }}
</el-descriptions-item>
<!-- 厂家 -->
<el-descriptions-item label="厂家">
{{ product.manufacturer || '--' }}
</el-descriptions-item>
</el-descriptions>
<!-- <BomInfo :bomId="product.bomId" /> -->
</el-dialog>
</div>
</template>
<script>
import { mapState } from 'vuex';
import BomInfo from './BomInfo.vue';
export default {
name: 'ProductInfo',
components: {
BomInfo
},
props: {
productId: {
type: [String, Number],
required: true
},
},
mounted() {
console.log(this.productId, this.productMap);
},
data() {
return {
showDetail: false,
// product: {},
};
},
computed: {
...mapState({
productMap: state => state.category.productMap
}),
product() {
// 检查 productMap 是否已加载
if (!this.productMap || Object.keys(this.productMap).length === 0) {
return {};
}
if (!this.productId) {
return {};
}
return this.productMap[this.productId.toString()] || {};
},
loading() {
return !this.productMap || Object.keys(this.productMap).length === 0
}
},
methods: {
clickHandle() {
this.showDetail = true;
}
},
// watch: {
// productId: {
// handler(newVal) {
// if (!newVal) {
// this.product = {};
// }
// const res = this.productMap[newVal.toString()] ? this.productMap[newVal.toString()] : {};
// this.product = res;
// },
// immediate: true
// }
// }
};
</script>
<style scoped>
.product-name {
color: #1890ff;
cursor: pointer;
text-decoration: underline;
}
/* 可选:调整描述列表的外边距 */
:deep(.el-descriptions) {
margin-top: -10px;
}
</style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div v-loading="loading" loading-text="加载中..."> <div>
<!-- 作用域插槽 --> <!-- 作用域插槽 -->
<span class="material-name" @click.stop="showDetail = true"> <span class="material-name" @click.stop="showDetail = true">
<slot name="default" :material="material"> <slot name="default" :material="material">
@@ -34,7 +34,6 @@
</template> </template>
<script> <script>
import { mapState } from 'vuex';
import BomInfo from './BomInfo.vue'; import BomInfo from './BomInfo.vue';
export default { export default {
@@ -43,8 +42,8 @@ export default {
BomInfo BomInfo
}, },
props: { props: {
materialId: { material: {
type: [String, Number], type: Object,
required: true required: true
} }
}, },
@@ -54,24 +53,24 @@ export default {
// material: {}, // material: {},
}; };
}, },
computed: { // computed: {
...mapState({ // ...mapState({
materialMap: state => state.category.rawMaterialMap // 假设vuex中为material模块 // materialMap: state => state.category.rawMaterialMap // 假设vuex中为material模块
}), // }),
material() { // material() {
// 检查 materialMap 是否已加载 // // 检查 materialMap 是否已加载
if (!this.materialMap || Object.keys(this.materialMap).length === 0) { // if (!this.materialMap || Object.keys(this.materialMap).length === 0) {
return {}; // return {};
} // }
if (!this.materialId) { // if (!this.materialId) {
return {}; // return {};
} // }
return this.materialMap[this.materialId.toString()] || {}; // return this.materialMap[this.materialId.toString()] || {};
}, // },
loading() { // loading() {
return !this.materialMap || Object.keys(this.materialMap).length === 0 // return !this.materialMap || Object.keys(this.materialMap).length === 0
} // }
}, // },
// watch: { // watch: {
// materialId: { // materialId: {
// handler: function (newVal) { // handler: function (newVal) {

View File

@@ -0,0 +1,84 @@
<template>
<div v-loading="loading" loading-text="加载中...">
<!-- 作用域插槽 -->
<span class="material-name" @click.stop="showDetail = true">
<slot name="default" :material="material">
{{ material.rawMaterialName ? material.rawMaterialName : '-' }}
</slot>
</span>
<el-dialog :visible="showDetail" @close="showDetail = false" :title="material.rawMaterialName" width="600px"
append-to-body>
<el-descriptions :column="1" border>
<!-- <el-descriptions-item label="原材料ID">{{ material.rawMaterialId }}</el-descriptions-item> -->
<el-descriptions-item label="原材料名称">{{ material.rawMaterialName }}</el-descriptions-item>
<el-descriptions-item label="原材料编码">{{ material.rawMaterialCode }}</el-descriptions-item>
<el-descriptions-item label="规格">{{ material.specification }}</el-descriptions-item>
<el-descriptions-item label="材质">
{{ material.material || '--' }}
</el-descriptions-item>
<el-descriptions-item label="表面处理">
{{ material.surfaceTreatment || '--' }}
</el-descriptions-item>
<!-- 锌层 -->
<el-descriptions-item label="锌层">
{{ material.zincLayer || '--' }}
</el-descriptions-item>
<!-- 厂家 -->
<el-descriptions-item label="厂家">
{{ material.manufacturer || '--' }}
</el-descriptions-item>
</el-descriptions>
<!-- <BomInfo :bomId="material.bomId" /> -->
</el-dialog>
</div>
</template>
<script>
import { mapState } from 'vuex';
import BomInfo from './BomInfo.vue';
export default {
name: 'RawMaterialInfo',
components: {
BomInfo
},
props: {
materialId: {
type: [String, Number],
required: true
}
},
data() {
return {
showDetail: false,
// material: {},
};
},
computed: {
...mapState({
materialMap: state => state.category.rawMaterialMap // 假设vuex中为material模块
}),
material() {
// 检查 materialMap 是否已加载
if (!this.materialMap || Object.keys(this.materialMap).length === 0) {
return {};
}
if (!this.materialId) {
return {};
}
return this.materialMap[this.materialId.toString()] || {};
},
loading() {
return !this.materialMap || Object.keys(this.materialMap).length === 0
}
},
}
</script>
<style scoped>
.material-name {
color: #1890ff;
cursor: pointer;
text-decoration: underline;
}
</style>

View File

@@ -64,12 +64,12 @@
min-width="250" min-width="250"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType === 'product'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType === 'product'" :product="scope.row.product">
<template #default="{ product }"> <template #default="{ product }">
{{ product.productName || '未知' }}({{ product.productCode || '无编码' }}) {{ product.productName || '未知' }}({{ product.productCode || '无编码' }})
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }"> <template #default="{ material }">
{{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }}) {{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }})
</template> </template>

View File

@@ -45,12 +45,12 @@
<!-- 物料ID/名称列仅物料统计时有效 --> <!-- 物料ID/名称列仅物料统计时有效 -->
<el-table-column v-if="queryParams.statType === '1'" label="物料信息" align="center" min-width="250"> <el-table-column v-if="queryParams.statType === '1'" label="物料信息" align="center" min-width="250">
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType === 'product'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType === 'product'" :product="scope.row.product">
<template #default="{ product }"> <template #default="{ product }">
{{ product.productName || '未知' }}({{ product.productCode || '无编码' }}) {{ product.productName || '未知' }}({{ product.productCode || '无编码' }})
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }"> <template #default="{ material }">
{{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }}) {{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }})
</template> </template>
@@ -104,12 +104,12 @@
</el-table-column> </el-table-column>
<el-table-column label="产品类型" align="center" min-width="250"> <el-table-column label="产品类型" align="center" min-width="250">
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType === 'product'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType === 'product'" :product="scope.row.product">
<template #default="{ product }"> <template #default="{ product }">
{{ product.itemName || '未知' }}({{ product.itemCode || '无编码' }}) {{ product.itemName || '未知' }}({{ product.itemCode || '无编码' }})
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }"> <template #default="{ material }">
{{ material.itemName || '未知' }}({{ material.itemCode || '无编码' }}) {{ material.itemName || '未知' }}({{ material.itemCode || '无编码' }})
</template> </template>

View File

@@ -64,12 +64,12 @@
min-width="250" min-width="250"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType === 'product'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType === 'product'" :product="scope.row.product">
<template #default="{ product }"> <template #default="{ product }">
{{ product.productName || '未知' }}({{ product.productCode || '无编码' }}) {{ product.productName || '未知' }}({{ product.productCode || '无编码' }})
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }"> <template #default="{ material }">
{{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }}) {{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }})
</template> </template>

View File

@@ -47,12 +47,12 @@
</el-table-column> </el-table-column>
<el-table-column label="产品类型" align="center" min-width="250"> <el-table-column label="产品类型" align="center" min-width="250">
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType === 'product'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType === 'product'" :product="scope.row.product">
<template #default="{ product }"> <template #default="{ product }">
{{ product.productName || '未知' }}({{ product.productCode || '无编码' }}) {{ product.productName || '未知' }}({{ product.productCode || '无编码' }})
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }"> <template #default="{ material }">
{{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }}) {{ material.rawMaterialName || '未知' }}({{ material.rawMaterialCode || '无编码' }})
</template> </template>

View File

@@ -84,12 +84,12 @@
<el-table-column label="物料类型" align="center" prop="materialType" /> <el-table-column label="物料类型" align="center" prop="materialType" />
<el-table-column label="产品类型" align="center" prop="itemName"> <el-table-column label="产品类型" align="center" prop="itemName">
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row.product">
<template slot-scope="{ product }"> <template slot-scope="{ product }">
{{ product.productName }}[{{ product.specification }}] {{ product.productName }}[{{ product.specification }}]
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template slot-scope="{ material }"> <template slot-scope="{ material }">
{{ material.rawMaterialName }}[{{ material.specification }}] {{ material.rawMaterialName }}[{{ material.specification }}]
</template> </template>

View File

@@ -38,12 +38,12 @@
</el-table-column> </el-table-column>
<el-table-column label="产品类型" align="center" prop="itemName"> <el-table-column label="产品类型" align="center" prop="itemName">
<template slot-scope="scope"> <template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product' || scope.row.itemType == 'semi'" :productId="scope.row.itemId"> <ProductInfo v-if="scope.row.itemType == 'product' || scope.row.itemType == 'semi'" :product="scope.row.product">
<template #default="{ product }"> <template #default="{ product }">
{{ product.productName }}({{ product.productCode }}) {{ product.productName }}({{ product.productCode }})
</template> </template>
</ProductInfo> </ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId"> <RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }"> <template #default="{ material }">
{{ material.rawMaterialName }}({{ material.rawMaterialCode }}) {{ material.rawMaterialName }}({{ material.rawMaterialCode }})
</template> </template>

View File

@@ -4,7 +4,7 @@
<div v-if="selectedTasks.length > 0" class="selected-tasks"> <div v-if="selectedTasks.length > 0" class="selected-tasks">
<div v-for="task in selectedTasks" :key="task.taskId" class="selected-task-item"> <div v-for="task in selectedTasks" :key="task.taskId" class="selected-task-item">
<div class="task-info"> <div class="task-info">
<ProductInfo :productId="task.productId" /> <ProductInfo :product="task.product" />
<CraftInfo :craftId="task.processId" /> <CraftInfo :craftId="task.processId" />
<span class="task-quantity">数量: {{ task.taskQuantity }}</span> <span class="task-quantity">数量: {{ task.taskQuantity }}</span>
</div> </div>