197 lines
5.4 KiB
Vue
197 lines
5.4 KiB
Vue
<template>
|
|
<div class="erp-report-page">
|
|
<section class="surface-panel">
|
|
<header class="surface-header">
|
|
<span>采购汇总</span>
|
|
<el-date-picker
|
|
v-model="range"
|
|
type="datetimerange"
|
|
value-format="yyyy-MM-dd HH:mm:ss"
|
|
size="small"
|
|
unlink-panels
|
|
start-placeholder="开始时间"
|
|
end-placeholder="结束时间"
|
|
@change="loadSummary"
|
|
/>
|
|
</header>
|
|
<div class="metrics-grid">
|
|
<div class="metric-card">
|
|
<p class="label">采购总金额</p>
|
|
<p class="value">¥{{ summary.totalAmount | formatMoney }}</p>
|
|
</div>
|
|
<div class="metric-card">
|
|
<p class="label">供应商数量</p>
|
|
<p class="value">{{ supplierBrief.length }}</p>
|
|
</div>
|
|
<div class="metric-card">
|
|
<p class="label">最大供应商金额</p>
|
|
<p class="value">¥{{ topSupplierAmount | formatMoney }}</p>
|
|
</div>
|
|
<div class="metric-card">
|
|
<p class="label">平均订单金额</p>
|
|
<p class="value">¥{{ avgSupplierAmount | formatMoney }}</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="surface-panel">
|
|
<div class="report-grid">
|
|
<div class="report-card">
|
|
<h4>供应商汇总</h4>
|
|
<el-table :data="supplierBrief" border size="small" height="320">
|
|
<el-table-column prop="supplierId" label="供应商ID" />
|
|
<el-table-column prop="orderCount" label="订单数" />
|
|
<el-table-column prop="totalAmount" label="总金额" />
|
|
</el-table>
|
|
</div>
|
|
<div class="report-card">
|
|
<h4>价格趋势</h4>
|
|
<el-table :data="priceTrend" border size="small" height="320">
|
|
<el-table-column prop="period" label="月份" />
|
|
<el-table-column prop="materialCode" label="物料编码" />
|
|
<el-table-column prop="avgPrice" label="平均含税价" />
|
|
</el-table>
|
|
</div>
|
|
<div class="report-card">
|
|
<h4>供应商退货率</h4>
|
|
<el-table :data="supplierQuality" border size="small" height="320">
|
|
<el-table-column prop="supplierId" label="供应商ID" />
|
|
<el-table-column prop="receivedQty" label="收货量" />
|
|
<el-table-column prop="returnQty" label="退货量" />
|
|
<el-table-column label="退货率">
|
|
<template slot-scope="scope">
|
|
{{ formatPercent(scope.row.returnRate) }}
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { getPurchaseReportSummary, getPurchasePriceTrend, getSupplierQuality } from '@/api/erp/purchase'
|
|
|
|
export default {
|
|
name: 'ErpPurchaseReport',
|
|
data() {
|
|
return {
|
|
range: [],
|
|
summary: { totalAmount: 0 },
|
|
supplierBrief: [],
|
|
priceTrend: [],
|
|
supplierQuality: []
|
|
}
|
|
},
|
|
filters: {
|
|
formatMoney(value) {
|
|
return Number(value || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
}
|
|
},
|
|
computed: {
|
|
topSupplierAmount() {
|
|
if (!this.supplierBrief.length) return 0
|
|
return Math.max(...this.supplierBrief.map(item => Number(item.totalAmount || 0)))
|
|
},
|
|
avgSupplierAmount() {
|
|
if (!this.supplierBrief.length) return 0
|
|
const sum = this.supplierBrief.reduce((acc, cur) => acc + Number(cur.totalAmount || 0), 0)
|
|
return sum / this.supplierBrief.length
|
|
}
|
|
},
|
|
created() {
|
|
this.loadSummary()
|
|
},
|
|
methods: {
|
|
buildParams() {
|
|
const params = {}
|
|
if (this.range && this.range.length === 2) {
|
|
params.beginTime = this.range[0]
|
|
params.endTime = this.range[1]
|
|
}
|
|
return params
|
|
},
|
|
loadSummary() {
|
|
const params = this.buildParams()
|
|
getPurchaseReportSummary(params).then(res => {
|
|
this.summary = { totalAmount: res.totalAmount || 0 }
|
|
this.supplierBrief = res.bySupplier || []
|
|
})
|
|
getPurchasePriceTrend(params).then(res => {
|
|
this.priceTrend = res || []
|
|
})
|
|
getSupplierQuality(params).then(res => {
|
|
this.supplierQuality = res || []
|
|
})
|
|
},
|
|
formatPercent(value) {
|
|
if (!value || !isFinite(value)) return '0%'
|
|
return `${(Number(value) * 100).toFixed(2)}%`
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.erp-report-page {
|
|
padding: 16px;
|
|
min-height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
}
|
|
.surface-panel {
|
|
background: #fff;
|
|
border: 1px solid #d6dce1;
|
|
border-radius: 4px;
|
|
padding: 16px;
|
|
}
|
|
.surface-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-weight: 600;
|
|
color: #1f2d3d;
|
|
margin-bottom: 12px;
|
|
}
|
|
.metrics-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 12px;
|
|
}
|
|
.metric-card {
|
|
border: 1px solid #d9dee4;
|
|
padding: 16px;
|
|
background: #f9fafb;
|
|
border-radius: 4px;
|
|
.label {
|
|
color: #5b6875;
|
|
margin-bottom: 6px;
|
|
}
|
|
.value {
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
color: #1f2d3d;
|
|
}
|
|
}
|
|
.report-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
gap: 12px;
|
|
}
|
|
.report-card {
|
|
border: 1px solid #d9dee4;
|
|
border-radius: 4px;
|
|
padding: 12px;
|
|
background: #fbfbfc;
|
|
h4 {
|
|
margin: 0 0 8px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #2b3440;
|
|
}
|
|
}
|
|
</style>
|
|
|