202 lines
6.8 KiB
Vue
202 lines
6.8 KiB
Vue
|
|
<template>
|
||
|
|
<div class="erp-requirement-page">
|
||
|
|
<el-card shadow="never" class="panel-card">
|
||
|
|
<div slot="header" class="panel-header">
|
||
|
|
<span>采购需求分析</span>
|
||
|
|
<div class="panel-actions">
|
||
|
|
<el-switch v-model="persistResult" active-text="写入建议表" inactive-text="仅计算" />
|
||
|
|
<el-button type="primary" size="mini" @click="handleAnalyze" :loading="loading">执行分析</el-button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<el-table :data="mappingRows" border size="small" class="mapping-table">
|
||
|
|
<el-table-column label="产品ID" width="180">
|
||
|
|
<template slot-scope="scope">
|
||
|
|
<el-input v-model="scope.row.productId" placeholder="产品ID" />
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
<el-table-column label="原料ID" width="180">
|
||
|
|
<template slot-scope="scope">
|
||
|
|
<el-input v-model="scope.row.rawMaterialId" placeholder="原料ID" />
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
<el-table-column label="转换率" width="160">
|
||
|
|
<template slot-scope="scope">
|
||
|
|
<el-input-number v-model="scope.row.conversionRate" :min="0" :max="1" :step="0.01" />
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
<el-table-column label="操作" width="120">
|
||
|
|
<template slot-scope="scope">
|
||
|
|
<el-button type="text" @click="removeMapping(scope.$index)" :disabled="mappingRows.length === 1">移除</el-button>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
</el-table>
|
||
|
|
<el-button icon="el-icon-plus" size="mini" class="add-btn" @click="addMapping">新增映射</el-button>
|
||
|
|
|
||
|
|
<el-table
|
||
|
|
:data="resultList"
|
||
|
|
border
|
||
|
|
size="small"
|
||
|
|
v-loading="loading"
|
||
|
|
show-summary
|
||
|
|
:summary-method="summaryMethod"
|
||
|
|
>
|
||
|
|
<el-table-column label="产品" width="220">
|
||
|
|
<template slot-scope="scope">
|
||
|
|
<div class="title">{{ scope.row.productName || '-' }}</div>
|
||
|
|
<div class="cell-sub">ID: {{ scope.row.productId || '-' }}</div>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
<el-table-column prop="specification" label="规格" width="160" />
|
||
|
|
<el-table-column prop="salesDemand" label="销售需求(吨)" width="140" />
|
||
|
|
<el-table-column prop="productStockWeight" label="成品库存(吨)" width="140" />
|
||
|
|
<el-table-column prop="rawStockConverted" label="原料折算(吨)" width="140" />
|
||
|
|
<el-table-column prop="inTransitConverted" label="在途折算(吨)" width="140" />
|
||
|
|
<el-table-column prop="pendingConverted" label="待下达折算(吨)" width="140" />
|
||
|
|
<el-table-column prop="suggestedPurchase" label="建议采购(吨)" width="160">
|
||
|
|
<template slot-scope="scope">
|
||
|
|
<span :class="scope.row.suggestedPurchase > 0 ? 'highlight' : ''">
|
||
|
|
{{ scope.row.suggestedPurchase }}
|
||
|
|
</span>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
</el-table>
|
||
|
|
</el-card>
|
||
|
|
|
||
|
|
<el-card shadow="never" class="panel-card">
|
||
|
|
<div slot="header" class="panel-header">
|
||
|
|
<span>原料折算详情</span>
|
||
|
|
</div>
|
||
|
|
<el-table :data="rawDetailData" size="small" border height="320">
|
||
|
|
<el-table-column prop="productName" label="产品" width="200" />
|
||
|
|
<el-table-column prop="rawMaterialName" label="原料" width="200" />
|
||
|
|
<el-table-column prop="conversionRate" label="转换率" width="120" />
|
||
|
|
<el-table-column prop="stockWeight" label="库存重量" width="120" />
|
||
|
|
<el-table-column prop="stockCoilCount" label="库存卷数" width="120" />
|
||
|
|
<el-table-column prop="convertedStock" label="折算库存" width="120" />
|
||
|
|
<el-table-column prop="convertedInTransit" label="折算在途" width="120" />
|
||
|
|
<el-table-column prop="convertedPending" label="折算待下达" width="120" />
|
||
|
|
</el-table>
|
||
|
|
</el-card>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
import { analyzePurchaseRequirement } from '@/api/erp/purchase'
|
||
|
|
|
||
|
|
export default {
|
||
|
|
name: 'ErpPurchaseRequirement',
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
mappingRows: [{ productId: '', rawMaterialId: '', conversionRate: 1 }],
|
||
|
|
persistResult: false,
|
||
|
|
loading: false,
|
||
|
|
resultList: [],
|
||
|
|
rawDetailData: []
|
||
|
|
}
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
addMapping() {
|
||
|
|
this.mappingRows.push({ productId: '', rawMaterialId: '', conversionRate: 1 })
|
||
|
|
},
|
||
|
|
removeMapping(idx) {
|
||
|
|
if (this.mappingRows.length === 1) return
|
||
|
|
this.mappingRows.splice(idx, 1)
|
||
|
|
},
|
||
|
|
handleAnalyze() {
|
||
|
|
if (!this.mappingRows.every(row => row.productId && row.rawMaterialId)) {
|
||
|
|
this.$message.warning('请完善产品与原料映射')
|
||
|
|
return
|
||
|
|
}
|
||
|
|
this.loading = true
|
||
|
|
analyzePurchaseRequirement({
|
||
|
|
persistResult: this.persistResult,
|
||
|
|
mappings: this.mappingRows.map(row => ({
|
||
|
|
productId: Number(row.productId),
|
||
|
|
rawMaterialId: Number(row.rawMaterialId),
|
||
|
|
conversionRate: Number(row.conversionRate || 0)
|
||
|
|
}))
|
||
|
|
})
|
||
|
|
.then(res => {
|
||
|
|
this.resultList = res || []
|
||
|
|
this.buildRawDetail(res || [])
|
||
|
|
this.$message.success('分析完成')
|
||
|
|
})
|
||
|
|
.finally(() => {
|
||
|
|
this.loading = false
|
||
|
|
})
|
||
|
|
},
|
||
|
|
buildRawDetail(list) {
|
||
|
|
const rows = []
|
||
|
|
list.forEach(item => {
|
||
|
|
(item.rawDetails || []).forEach(detail => {
|
||
|
|
rows.push({
|
||
|
|
productName: item.productName,
|
||
|
|
rawMaterialName: detail.rawMaterialName,
|
||
|
|
conversionRate: detail.conversionRate,
|
||
|
|
stockWeight: detail.stockWeight,
|
||
|
|
stockCoilCount: detail.stockCoilCount,
|
||
|
|
convertedStock: detail.convertedStock,
|
||
|
|
convertedInTransit: detail.convertedInTransit,
|
||
|
|
convertedPending: detail.convertedPending
|
||
|
|
})
|
||
|
|
})
|
||
|
|
})
|
||
|
|
this.rawDetailData = rows
|
||
|
|
},
|
||
|
|
summaryMethod({ data }) {
|
||
|
|
const sums = []
|
||
|
|
const fields = ['salesDemand', 'productStockWeight', 'rawStockConverted', 'inTransitConverted', 'pendingConverted', 'suggestedPurchase']
|
||
|
|
sums[0] = '总计'
|
||
|
|
data.forEach(row => {
|
||
|
|
fields.forEach((field, idx) => {
|
||
|
|
sums[idx + 2] = (Number(sums[idx + 2]) || 0) + Number(row[field] || 0)
|
||
|
|
})
|
||
|
|
})
|
||
|
|
return sums
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss" scoped>
|
||
|
|
.erp-requirement-page {
|
||
|
|
padding: 16px;
|
||
|
|
background: #eef1f3;
|
||
|
|
min-height: 100%;
|
||
|
|
}
|
||
|
|
.panel-card {
|
||
|
|
margin-bottom: 18px;
|
||
|
|
border: 1px solid #d0d5d8;
|
||
|
|
}
|
||
|
|
.panel-header {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: center;
|
||
|
|
font-weight: 600;
|
||
|
|
color: #1f2d3d;
|
||
|
|
}
|
||
|
|
.panel-actions {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
.mapping-table {
|
||
|
|
margin-bottom: 8px;
|
||
|
|
}
|
||
|
|
.add-btn {
|
||
|
|
margin-bottom: 12px;
|
||
|
|
}
|
||
|
|
.title {
|
||
|
|
font-weight: 600;
|
||
|
|
}
|
||
|
|
.cell-sub {
|
||
|
|
color: #7c8792;
|
||
|
|
font-size: 12px;
|
||
|
|
}
|
||
|
|
.highlight {
|
||
|
|
color: #c0392b;
|
||
|
|
font-weight: 600;
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
|