辅材修正,详情页迭代,产品图片加载
This commit is contained in:
360
gear-ui3/src/views/mat/product/detail.vue
Normal file
360
gear-ui3/src/views/mat/product/detail.vue
Normal file
@@ -0,0 +1,360 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="mb20">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ productDetail.productName }} - 产品详情</span>
|
||||
<el-button type="primary" plain size="small" @click="handleBack">返回列表</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="product-info">
|
||||
<div class="info-item">
|
||||
<span class="label">产品名称:</span>
|
||||
<span class="value">{{ productDetail.productName }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">产品规格:</span>
|
||||
<span class="value">{{ productDetail.spec }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">产品型号:</span>
|
||||
<span class="value">{{ productDetail.model }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">产品单价:</span>
|
||||
<span class="value">{{ formatDecimal(productDetail.unitPrice) }} 元</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">备注:</span>
|
||||
<span class="value">{{ productDetail.remark || '无' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 产品附加属性 -->
|
||||
<div class="product-addition" v-if="productAdditionList.length > 0">
|
||||
<h4>产品附加属性</h4>
|
||||
<el-table :data="productAdditionList" style="width: 100%" border>
|
||||
<el-table-column prop="attrName" label="属性名" width="150" />
|
||||
<el-table-column prop="attrValue" label="属性值" />
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="product-images" v-if="productDetail.productImages && productDetail.productImages.trim()">
|
||||
<h4>产品图片</h4>
|
||||
<div class="image-list">
|
||||
<el-image
|
||||
v-for="(image, index) in productDetail.productImages.split(',').filter(img => img.trim())"
|
||||
:key="index"
|
||||
:src="image"
|
||||
:preview-src-list="productDetail.productImages.split(',').filter(img => img.trim())"
|
||||
style="width: 100px; height: 100px; margin-right: 10px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>材料明细</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 主材部分 -->
|
||||
<div class="material-section">
|
||||
<h3 class="section-title">主材</h3>
|
||||
<el-table :data="mainMaterials" style="width: 100%" border>
|
||||
<el-table-column prop="materialName" label="配料名称" width="150" />
|
||||
<el-table-column prop="spec" label="材料规格" width="150" />
|
||||
<el-table-column prop="quantity" label="数量" width="100" align="center" />
|
||||
<el-table-column prop="price" label="价格" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ formatDecimal(scope.row.price) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="subtotal" label="小计" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ formatDecimal(scope.row.subtotal) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="section-summary" v-if="mainMaterials.length > 0">
|
||||
<span>主材小计:{{ formatDecimal(mainMaterials.reduce((sum, item) => sum + item.subtotal, 0)) }} 元</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 辅材部分 -->
|
||||
<div class="material-section">
|
||||
<h3 class="section-title">辅材</h3>
|
||||
<el-table :data="auxiliaryMaterials" style="width: 100%" border>
|
||||
<el-table-column prop="materialName" label="配料名称" width="150" />
|
||||
<el-table-column prop="spec" label="材料规格" width="150" />
|
||||
<el-table-column prop="quantity" label="数量" width="100" align="center" />
|
||||
<el-table-column prop="price" label="价格" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ formatDecimal(scope.row.price) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="subtotal" label="小计" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ formatDecimal(scope.row.subtotal) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="section-summary" v-if="auxiliaryMaterials.length > 0">
|
||||
<span>辅材小计:{{ formatDecimal(auxiliaryMaterials.reduce((sum, item) => sum + item.subtotal, 0)) }} 元</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工本部分 -->
|
||||
<div class="material-section">
|
||||
<h3 class="section-title">工本</h3>
|
||||
<el-table :data="laborMaterials" style="width: 100%" border>
|
||||
<el-table-column prop="materialName" label="项目名称" width="150" />
|
||||
<el-table-column prop="spec" label="规格" width="150" />
|
||||
<el-table-column prop="quantity" label="数量" width="100" align="center" />
|
||||
<el-table-column prop="price" label="价格" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ formatDecimal(scope.row.price) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="subtotal" label="小计" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ formatDecimal(scope.row.subtotal) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="section-summary" v-if="laborMaterials.length > 0">
|
||||
<span>工本小计:{{ formatDecimal(laborMaterials.reduce((sum, item) => sum + item.subtotal, 0)) }} 元</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 总计部分 -->
|
||||
<div class="total-section">
|
||||
<div class="total-item">
|
||||
<span class="total-label">合计:</span>
|
||||
<span class="total-value">{{ formatDecimal(totalAmount) }} 元</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ProductDetail">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { getProduct } from "@/api/mat/product";
|
||||
import { listProductMaterialRelation } from "@/api/mat/productMaterialRelation";
|
||||
import { getMaterial } from "@/api/mat/material";
|
||||
import { listProductAddition } from "@/api/mat/productAddition";
|
||||
import { formatDecimal } from '@/utils/gear';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const productDetail = ref({});
|
||||
const loading = ref(true);
|
||||
const materialLoading = ref(false);
|
||||
const additionLoading = ref(false);
|
||||
|
||||
// 材料明细数据
|
||||
const productMaterialRelationList = ref([]);
|
||||
|
||||
// 产品附加属性数据
|
||||
const productAdditionList = ref([]);
|
||||
|
||||
// 计算主材、辅材和工本
|
||||
const mainMaterials = computed(() => {
|
||||
// 主材(材料类型为2)
|
||||
return productMaterialRelationList.value
|
||||
.filter(item => item.material && item.material.materialType === 2)
|
||||
.map(item => ({
|
||||
materialName: item.material.materialName,
|
||||
spec: item.material.spec,
|
||||
quantity: item.materialNum + (item.material.unit || ''),
|
||||
price: item.material.unitPrice || 0,
|
||||
subtotal: (item.materialNum * (item.material.unitPrice || 0)) || 0
|
||||
}));
|
||||
});
|
||||
|
||||
const auxiliaryMaterials = computed(() => {
|
||||
// 辅材(材料类型为1)
|
||||
return productMaterialRelationList.value
|
||||
.filter(item => item.material && item.material.materialType === 1)
|
||||
.map(item => ({
|
||||
materialName: item.material.materialName,
|
||||
spec: item.material.spec,
|
||||
quantity: item.materialNum + (item.material.unit || ''),
|
||||
price: item.material.unitPrice || 0,
|
||||
subtotal: (item.materialNum * (item.material.unitPrice || 0)) || 0
|
||||
}));
|
||||
});
|
||||
|
||||
const laborMaterials = computed(() => {
|
||||
// 工本(材料类型为3)
|
||||
return productMaterialRelationList.value
|
||||
.filter(item => item.material && item.material.materialType === 3)
|
||||
.map(item => ({
|
||||
materialName: item.material.materialName,
|
||||
spec: item.material.spec,
|
||||
quantity: item.materialNum + (item.material.unit || ''),
|
||||
price: item.material.unitPrice || 0,
|
||||
subtotal: (item.materialNum * (item.material.unitPrice || 0)) || 0
|
||||
}));
|
||||
});
|
||||
|
||||
// 计算总金额
|
||||
const totalAmount = computed(() => {
|
||||
const mainTotal = mainMaterials.value.reduce((sum, item) => sum + item.subtotal, 0);
|
||||
const auxiliaryTotal = auxiliaryMaterials.value.reduce((sum, item) => sum + item.subtotal, 0);
|
||||
const laborTotal = laborMaterials.value.reduce((sum, item) => sum + item.subtotal, 0);
|
||||
return mainTotal + auxiliaryTotal + laborTotal;
|
||||
});
|
||||
|
||||
// 获取产品详情
|
||||
function getProductDetail() {
|
||||
const productId = route.params.id;
|
||||
if (productId) {
|
||||
loading.value = true;
|
||||
getProduct(productId).then(response => {
|
||||
productDetail.value = response.data;
|
||||
loading.value = false;
|
||||
// 获取材料明细
|
||||
getMaterialDetail(productId);
|
||||
// 获取产品附加属性
|
||||
getProductAddition(productId);
|
||||
}).catch(error => {
|
||||
console.error('获取产品详情失败:', error);
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取产品附加属性
|
||||
function getProductAddition(productId) {
|
||||
additionLoading.value = true;
|
||||
listProductAddition({ productId }).then(response => {
|
||||
productAdditionList.value = response.rows || [];
|
||||
additionLoading.value = false;
|
||||
}).catch(error => {
|
||||
console.error('获取产品附加属性失败:', error);
|
||||
additionLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 获取材料明细
|
||||
function getMaterialDetail(productId) {
|
||||
materialLoading.value = true;
|
||||
listProductMaterialRelation({ productId }).then(response => {
|
||||
const relations = response.rows;
|
||||
// 为每个配方项获取物料详细信息,包括物料类型
|
||||
const promises = relations.map(item => {
|
||||
return getMaterial(item.materialId).then(materialResponse => {
|
||||
item.material = materialResponse.data;
|
||||
return item;
|
||||
});
|
||||
});
|
||||
return Promise.all(promises);
|
||||
}).then(relationsWithMaterial => {
|
||||
productMaterialRelationList.value = relationsWithMaterial;
|
||||
materialLoading.value = false;
|
||||
}).catch(error => {
|
||||
console.error('获取材料明细失败:', error);
|
||||
materialLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 返回列表
|
||||
function handleBack() {
|
||||
router.back();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getProductDetail();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.product-images {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.image-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.material-section {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
background-color: #f5f7fa;
|
||||
padding: 10px;
|
||||
border-left: 4px solid #409eff;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.section-summary {
|
||||
text-align: right;
|
||||
padding: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
margin-top: -1px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.total-section {
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
background-color: #f0f9eb;
|
||||
border: 1px solid #b7eb8f;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.total-item {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.total-label {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.total-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #f56c6c;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user