提交基础采购

This commit is contained in:
2025-11-18 16:45:05 +08:00
parent cc9b1c0e92
commit 7c04e13198
77 changed files with 5733 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
<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>