Files
GEAR-OA/gear-ui3/src/views/mat/product/detail.vue

360 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>