feat(wms): 新增带改判记录的钢卷列表接口及展示
feat(crm): 在订单异议页面添加异议内容和处理结果的HTML展示 feat(erp): 新增ERP仪表盘页面并优化采购订单表单 refactor(wms): 移除钢卷列表中的冗余代码并添加改判原因列
This commit is contained in:
@@ -424,3 +424,15 @@ export function fixMismatchedItemCoil(coilId) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带有改判记录的钢卷列表
|
||||||
|
*/
|
||||||
|
export function listWithAdjustRecordCoil(params) {
|
||||||
|
return request({
|
||||||
|
url: '/wms/materialCoil/listWithRejudge',
|
||||||
|
method: 'get',
|
||||||
|
timeout: 600000,
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,7 +27,11 @@
|
|||||||
<el-table v-loading="loading" :data="salesObjectionList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="salesObjectionList" @selection-change="handleSelectionChange">
|
||||||
<el-table-column label="编号" align="center" prop="objectionCode" />
|
<el-table-column label="编号" align="center" prop="objectionCode" />
|
||||||
<!-- <el-table-column label="异议类型" align="center" prop="objectionType" /> -->
|
<!-- <el-table-column label="异议类型" align="center" prop="objectionType" /> -->
|
||||||
<!-- <el-table-column label="异议内容" align="center" prop="objectionContent" /> -->
|
<el-table-column label="异议内容" align="center" prop="objectionContent" show-overflow-tooltip>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div class="cell-html" v-html="scope.row.objectionContent"></div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" align="center" prop="objectionStatus">
|
<el-table-column label="状态" align="center" prop="objectionStatus">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag v-if="scope.row.objectionStatus === 0" type="danger">待处理</el-tag>
|
<el-tag v-if="scope.row.objectionStatus === 0" type="danger">待处理</el-tag>
|
||||||
@@ -35,7 +39,11 @@
|
|||||||
<el-tag v-else-if="scope.row.objectionStatus === 2" type="info">已关闭</el-tag>
|
<el-tag v-else-if="scope.row.objectionStatus === 2" type="info">已关闭</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- <el-table-column label="处理内容" align="center" prop="handleContent" /> -->
|
<el-table-column label="处理结果" align="center" prop="handleContent" show-overflow-tooltip>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div class="cell-html" v-html="scope.row.handleContent"></div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="处理人" align="center" prop="handleUser" />
|
<el-table-column label="处理人" align="center" prop="handleUser" />
|
||||||
<el-table-column label="处理时间" align="center" prop="handleTime" width="180">
|
<el-table-column label="处理时间" align="center" prop="handleTime" width="180">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
@@ -133,8 +141,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog title="查看处理结果" :visible.sync="viewOpen" width="500px" append-to-body>
|
<el-dialog title="查看处理结果" :visible.sync="viewOpen" width="700px" append-to-body>
|
||||||
<el-descriptions :column="2" :data="viewForm" label-width="80px">
|
<el-descriptions :column="2" :data="viewForm" label-width="80px" border>
|
||||||
|
<el-descriptions-item label="异议编号" :span="2">{{ viewForm.objectionCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="异议内容" :span="2">
|
||||||
|
<div v-html="viewForm.objectionContent"></div>
|
||||||
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="处理人">{{ viewForm.handleUser }}</el-descriptions-item>
|
<el-descriptions-item label="处理人">{{ viewForm.handleUser }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="处理时间">{{ viewForm.handleTime }}</el-descriptions-item>
|
<el-descriptions-item label="处理时间">{{ viewForm.handleTime }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="处理内容" :span="2">
|
<el-descriptions-item label="处理内容" :span="2">
|
||||||
@@ -379,3 +391,12 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.cell-html {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
804
klp-ui/src/views/erp/dashboard/index.vue
Normal file
804
klp-ui/src/views/erp/dashboard/index.vue
Normal file
@@ -0,0 +1,804 @@
|
|||||||
|
<template>
|
||||||
|
<div class="erp-dashboard-page">
|
||||||
|
<section class="stats-section">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon supplier-icon">
|
||||||
|
<i class="el-icon-office-building"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-value">{{ supplierCount }}</div>
|
||||||
|
<div class="stat-label">供应商总数</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon order-icon">
|
||||||
|
<i class="el-icon-document"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-value">{{ orderCount }}</div>
|
||||||
|
<div class="stat-label">采购订单总数</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon doing-icon">
|
||||||
|
<i class="el-icon-loading"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-value">{{ doingOrderCount }}</div>
|
||||||
|
<div class="stat-label">执行中订单</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon complete-icon">
|
||||||
|
<i class="el-icon-circle-check"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-value">{{ completeOrderCount }}</div>
|
||||||
|
<div class="stat-label">已完成订单</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="charts-section">
|
||||||
|
<div class="chart-card">
|
||||||
|
<div class="chart-header">
|
||||||
|
<span>订单状态分布</span>
|
||||||
|
</div>
|
||||||
|
<div ref="statusChart" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-card">
|
||||||
|
<div class="chart-header">
|
||||||
|
<span>供应商订单占比</span>
|
||||||
|
</div>
|
||||||
|
<div ref="supplierChart" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="charts-section">
|
||||||
|
<div class="chart-card full-width">
|
||||||
|
<div class="chart-header">
|
||||||
|
<span>订单趋势(按天)</span>
|
||||||
|
</div>
|
||||||
|
<div ref="trendChart" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="charts-section">
|
||||||
|
<div class="chart-card">
|
||||||
|
<div class="chart-header">
|
||||||
|
<span>业务员订单分布</span>
|
||||||
|
</div>
|
||||||
|
<div ref="salesmanBarChart" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-card">
|
||||||
|
<div class="chart-header">
|
||||||
|
<span>部门订单分布</span>
|
||||||
|
</div>
|
||||||
|
<div ref="deptBarChart" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-card">
|
||||||
|
<div class="chart-header">
|
||||||
|
<span>按业务员订单占比(饼图)</span>
|
||||||
|
</div>
|
||||||
|
<div ref="salesmanPieChart" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-card">
|
||||||
|
<div class="chart-header">
|
||||||
|
<span>按部门订单占比(饼图)</span>
|
||||||
|
</div>
|
||||||
|
<div ref="deptPieChart" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="charts-section">
|
||||||
|
<div class="chart-card">
|
||||||
|
<div class="chart-header">
|
||||||
|
<span>供应商信用等级分布</span>
|
||||||
|
</div>
|
||||||
|
<div ref="creditChart" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-card">
|
||||||
|
<div class="chart-header">
|
||||||
|
<span>供应商类型分布</span>
|
||||||
|
</div>
|
||||||
|
<div ref="typeChart" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listSupplier, listPurchaseOrder } from '@/api/erp/purchase'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ErpDashboard',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
supplierCount: 0,
|
||||||
|
orderCount: 0,
|
||||||
|
doingOrderCount: 0,
|
||||||
|
completeOrderCount: 0,
|
||||||
|
supplierList: [],
|
||||||
|
orderList: [],
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.statusChart) this.statusChart.dispose()
|
||||||
|
if (this.supplierChart) this.supplierChart.dispose()
|
||||||
|
if (this.trendChart) this.trendChart.dispose()
|
||||||
|
if (this.creditChart) this.creditChart.dispose()
|
||||||
|
if (this.typeChart) this.typeChart.dispose()
|
||||||
|
if (this.salesmanBarChart) this.salesmanBarChart.dispose()
|
||||||
|
if (this.salesmanPieChart) this.salesmanPieChart.dispose()
|
||||||
|
if (this.deptBarChart) this.deptBarChart.dispose()
|
||||||
|
if (this.deptPieChart) this.deptPieChart.dispose()
|
||||||
|
if (this.supplierBarChart) this.supplierBarChart.dispose()
|
||||||
|
if (this.supplierPieChart) this.supplierPieChart.dispose()
|
||||||
|
window.removeEventListener('resize', this.handleResize)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadData() {
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const [supplierRes, orderRes] = await Promise.all([
|
||||||
|
listSupplier({ pageNum: 1, pageSize: 100000 }),
|
||||||
|
listPurchaseOrder({ pageNum: 1, pageSize: 100000 })
|
||||||
|
])
|
||||||
|
this.supplierList = supplierRes.rows || []
|
||||||
|
this.orderList = orderRes.rows || []
|
||||||
|
this.supplierCount = this.supplierList.length
|
||||||
|
this.orderCount = this.orderList.length
|
||||||
|
this.doingOrderCount = this.orderList.filter(o => o.orderStatus === 1).length
|
||||||
|
this.completeOrderCount = this.orderList.filter(o => o.orderStatus === 3).length
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.initCharts()
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('加载数据失败')
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initCharts() {
|
||||||
|
this.initStatusChart()
|
||||||
|
this.initSupplierChart()
|
||||||
|
this.initTrendChart()
|
||||||
|
this.initCreditChart()
|
||||||
|
this.initTypeChart()
|
||||||
|
this.initSalesmanCharts()
|
||||||
|
this.initDeptCharts()
|
||||||
|
this.initSupplierNameCharts()
|
||||||
|
window.addEventListener('resize', this.handleResize)
|
||||||
|
},
|
||||||
|
handleResize() {
|
||||||
|
this.statusChart && this.statusChart.resize()
|
||||||
|
this.supplierChart && this.supplierChart.resize()
|
||||||
|
this.trendChart && this.trendChart.resize()
|
||||||
|
this.creditChart && this.creditChart.resize()
|
||||||
|
this.typeChart && this.typeChart.resize()
|
||||||
|
this.salesmanBarChart && this.salesmanBarChart.resize()
|
||||||
|
this.salesmanPieChart && this.salesmanPieChart.resize()
|
||||||
|
this.deptBarChart && this.deptBarChart.resize()
|
||||||
|
this.deptPieChart && this.deptPieChart.resize()
|
||||||
|
this.supplierBarChart && this.supplierBarChart.resize()
|
||||||
|
this.supplierPieChart && this.supplierPieChart.resize()
|
||||||
|
},
|
||||||
|
initStatusChart() {
|
||||||
|
this.statusChart = echarts.init(this.$refs.statusChart)
|
||||||
|
const statusMap = {
|
||||||
|
0: '草稿',
|
||||||
|
1: '执行中',
|
||||||
|
2: '部分到货',
|
||||||
|
3: '已完成',
|
||||||
|
4: '已取消'
|
||||||
|
}
|
||||||
|
const statusData = {}
|
||||||
|
this.orderList.forEach(order => {
|
||||||
|
const status = order.orderStatus || 0
|
||||||
|
statusData[status] = (statusData[status] || 0) + 1
|
||||||
|
})
|
||||||
|
const data = Object.keys(statusData).map(key => ({
|
||||||
|
name: statusMap[key],
|
||||||
|
value: statusData[key]
|
||||||
|
}))
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'left'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '订单状态',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 10,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
formatter: '{b}: {c}'
|
||||||
|
},
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.statusChart.setOption(option)
|
||||||
|
},
|
||||||
|
initSupplierChart() {
|
||||||
|
this.supplierChart = echarts.init(this.$refs.supplierChart)
|
||||||
|
const supplierOrderCount = {}
|
||||||
|
this.orderList.forEach(order => {
|
||||||
|
const supplierName = order.supplierName || '未知'
|
||||||
|
supplierOrderCount[supplierName] = (supplierOrderCount[supplierName] || 0) + 1
|
||||||
|
})
|
||||||
|
let data = Object.keys(supplierOrderCount).map(name => ({
|
||||||
|
name,
|
||||||
|
value: supplierOrderCount[name]
|
||||||
|
}))
|
||||||
|
data = data.sort((a, b) => b.value - a.value).slice(0, 10)
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'left'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '供应商订单',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 10,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
formatter: '{b}: {c}'
|
||||||
|
},
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.supplierChart.setOption(option)
|
||||||
|
},
|
||||||
|
initTrendChart() {
|
||||||
|
this.trendChart = echarts.init(this.$refs.trendChart)
|
||||||
|
const dayData = {}
|
||||||
|
this.orderList.forEach(order => {
|
||||||
|
if (order.orderDate) {
|
||||||
|
const day = order.orderDate
|
||||||
|
dayData[day] = (dayData[day] || 0) + 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const days = Object.keys(dayData).sort()
|
||||||
|
const counts = days.map(d => dayData[d])
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['订单数量']
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: days,
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 45
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '订单数量',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: counts,
|
||||||
|
areaStyle: {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{ offset: 0, color: 'rgba(80, 141, 255, 0.5)' },
|
||||||
|
{ offset: 1, color: 'rgba(80, 141, 255, 0.1)' }
|
||||||
|
])
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
color: '#508dff'
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#508dff'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.trendChart.setOption(option)
|
||||||
|
},
|
||||||
|
initCreditChart() {
|
||||||
|
this.creditChart = echarts.init(this.$refs.creditChart)
|
||||||
|
const creditData = {}
|
||||||
|
this.supplierList.forEach(supplier => {
|
||||||
|
const credit = supplier.creditRating || '-'
|
||||||
|
creditData[credit] = (creditData[credit] || 0) + 1
|
||||||
|
})
|
||||||
|
const data = Object.keys(creditData).map(key => ({
|
||||||
|
name: key,
|
||||||
|
value: creditData[key]
|
||||||
|
}))
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: Object.keys(creditData)
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '供应商数量',
|
||||||
|
type: 'bar',
|
||||||
|
data: Object.values(creditData),
|
||||||
|
itemStyle: {
|
||||||
|
color: function (params) {
|
||||||
|
const colorMap = {
|
||||||
|
'A': '#67c23a',
|
||||||
|
'B': '#e6a23c',
|
||||||
|
'C': '#f56c6c',
|
||||||
|
'D': '#909399',
|
||||||
|
'-': '#409eff'
|
||||||
|
}
|
||||||
|
return colorMap[params.name] || '#409eff'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.creditChart.setOption(option)
|
||||||
|
},
|
||||||
|
initTypeChart() {
|
||||||
|
this.typeChart = echarts.init(this.$refs.typeChart)
|
||||||
|
const typeData = {}
|
||||||
|
this.supplierList.forEach(supplier => {
|
||||||
|
const type = supplier.type === 'RAW' ? '原料供应商' : '其他供应商'
|
||||||
|
typeData[type] = (typeData[type] || 0) + 1
|
||||||
|
})
|
||||||
|
const data = Object.keys(typeData).map(key => ({
|
||||||
|
name: key,
|
||||||
|
value: typeData[key]
|
||||||
|
}))
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'left'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '供应商类型',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 10,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
formatter: '{b}: {c}'
|
||||||
|
},
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.typeChart.setOption(option)
|
||||||
|
},
|
||||||
|
initSalesmanCharts() {
|
||||||
|
// 柱状图
|
||||||
|
this.salesmanBarChart = echarts.init(this.$refs.salesmanBarChart)
|
||||||
|
const salesmanData = {}
|
||||||
|
this.orderList.forEach(order => {
|
||||||
|
const salesman = order.salesman || '未知'
|
||||||
|
salesmanData[salesman] = (salesmanData[salesman] || 0) + 1
|
||||||
|
})
|
||||||
|
const salesmanList = Object.keys(salesmanData).sort((a, b) => salesmanData[b] - salesmanData[a])
|
||||||
|
const salesmanCounts = salesmanList.map(s => salesmanData[s])
|
||||||
|
|
||||||
|
const barOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: salesmanList,
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 45
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '订单数量',
|
||||||
|
type: 'bar',
|
||||||
|
data: salesmanCounts,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#67c23a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.salesmanBarChart.setOption(barOption)
|
||||||
|
|
||||||
|
// 饼图
|
||||||
|
this.salesmanPieChart = echarts.init(this.$refs.salesmanPieChart)
|
||||||
|
let pieData = salesmanList.map(name => ({
|
||||||
|
name,
|
||||||
|
value: salesmanData[name]
|
||||||
|
}))
|
||||||
|
pieData = pieData.slice(0, 10)
|
||||||
|
|
||||||
|
const pieOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'left'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '业务员订单',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 10,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
formatter: '{b}: {c}'
|
||||||
|
},
|
||||||
|
data: pieData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.salesmanPieChart.setOption(pieOption)
|
||||||
|
},
|
||||||
|
initDeptCharts() {
|
||||||
|
// 柱状图
|
||||||
|
this.deptBarChart = echarts.init(this.$refs.deptBarChart)
|
||||||
|
const deptData = {}
|
||||||
|
this.orderList.forEach(order => {
|
||||||
|
const dept = order.deptName || '未知'
|
||||||
|
deptData[dept] = (deptData[dept] || 0) + 1
|
||||||
|
})
|
||||||
|
const deptList = Object.keys(deptData).sort((a, b) => deptData[b] - deptData[a])
|
||||||
|
const deptCounts = deptList.map(d => deptData[d])
|
||||||
|
|
||||||
|
const barOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: deptList,
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 45
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '订单数量',
|
||||||
|
type: 'bar',
|
||||||
|
data: deptCounts,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#e6a23c'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.deptBarChart.setOption(barOption)
|
||||||
|
|
||||||
|
// 饼图
|
||||||
|
this.deptPieChart = echarts.init(this.$refs.deptPieChart)
|
||||||
|
let pieData = deptList.map(name => ({
|
||||||
|
name,
|
||||||
|
value: deptData[name]
|
||||||
|
}))
|
||||||
|
pieData = pieData.slice(0, 10)
|
||||||
|
|
||||||
|
const pieOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'left'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '部门订单',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 10,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
formatter: '{b}: {c}'
|
||||||
|
},
|
||||||
|
data: pieData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.deptPieChart.setOption(pieOption)
|
||||||
|
},
|
||||||
|
initSupplierNameCharts() {
|
||||||
|
// 柱状图
|
||||||
|
this.supplierBarChart = echarts.init(this.$refs.supplierBarChart)
|
||||||
|
const supplierData = {}
|
||||||
|
this.orderList.forEach(order => {
|
||||||
|
const supplier = order.supplierName || '未知'
|
||||||
|
supplierData[supplier] = (supplierData[supplier] || 0) + 1
|
||||||
|
})
|
||||||
|
const supplierList = Object.keys(supplierData).sort((a, b) => supplierData[b] - supplierData[a])
|
||||||
|
const supplierCounts = supplierList.map(s => supplierData[s])
|
||||||
|
|
||||||
|
const barOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: supplierList.slice(0, 15),
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 45
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '订单数量',
|
||||||
|
type: 'bar',
|
||||||
|
data: supplierCounts.slice(0, 15),
|
||||||
|
itemStyle: {
|
||||||
|
color: '#f56c6c'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.supplierBarChart.setOption(barOption)
|
||||||
|
|
||||||
|
// 饼图
|
||||||
|
this.supplierPieChart = echarts.init(this.$refs.supplierPieChart)
|
||||||
|
let pieData = supplierList.map(name => ({
|
||||||
|
name,
|
||||||
|
value: supplierData[name]
|
||||||
|
}))
|
||||||
|
pieData = pieData.slice(0, 10)
|
||||||
|
|
||||||
|
const pieOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'left'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '供应商订单',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 10,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
formatter: '{b}: {c}'
|
||||||
|
},
|
||||||
|
data: pieData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.supplierPieChart.setOption(pieOption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.erp-dashboard-page {
|
||||||
|
padding: 16px;
|
||||||
|
min-height: 100%;
|
||||||
|
background: #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-section {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 28px;
|
||||||
|
|
||||||
|
&.supplier-icon {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.order-icon {
|
||||||
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.doing-icon {
|
||||||
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.complete-icon {
|
||||||
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1f2a37;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #7a8694;
|
||||||
|
}
|
||||||
|
|
||||||
|
.charts-section {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&.full-width {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-header {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2a37;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #e4e7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
height: 300px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
<span>采购订单列表</span>
|
<span>采购订单列表</span>
|
||||||
<div class="surface-actions">
|
<div class="surface-actions">
|
||||||
<el-button type="primary" size="small" @click="openOrderDialog()">新增订单</el-button>
|
<el-button type="primary" size="small" @click="openOrderDialog()">新增订单</el-button>
|
||||||
<el-button size="small" @click="openItemDialog()">维护明细</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="inline-filter">
|
<div class="inline-filter">
|
||||||
@@ -51,26 +50,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-table :data="orderList" border stripe highlight-current-row size="small" v-loading="orderLoading">
|
<el-table :data="orderList" border stripe highlight-current-row size="small" v-loading="orderLoading">
|
||||||
<el-table-column prop="orderCode" label="订单编号" width="150" fixed="left" />
|
<el-table-column prop="orderCode" label="订单编号" width="140" fixed="left" />
|
||||||
<el-table-column prop="supplierName" label="供应商" />
|
<el-table-column prop="supplierName" label="供应商" width="150" />
|
||||||
<el-table-column prop="orderDate" label="下单日期" />
|
<el-table-column prop="billType" label="单据类型" width="100" />
|
||||||
<el-table-column prop="expectedArrival" label="期望到货" />
|
<el-table-column prop="receiveType" label="收发类别" width="100" />
|
||||||
<el-table-column prop="orderType" label="类型" />
|
<el-table-column prop="deptName" label="部门" width="100" />
|
||||||
<el-table-column prop="totalAmount" label="金额" />
|
<el-table-column prop="salesman" label="业务员" width="80" />
|
||||||
<el-table-column label="状态" width="120">
|
<el-table-column prop="orderDate" label="下单日期" width="100" />
|
||||||
|
<el-table-column prop="expectedArrival" label="期望到货" width="100" />
|
||||||
|
<el-table-column prop="orderType" label="订单类型" width="100" />
|
||||||
|
<el-table-column prop="totalAmount" label="金额" width="100" />
|
||||||
|
<el-table-column prop="maker" label="制单人" width="80" />
|
||||||
|
<el-table-column prop="auditor" label="审核人" width="80" />
|
||||||
|
<el-table-column prop="bookkeeper" label="记账人" width="80" />
|
||||||
|
<el-table-column prop="auditDate" label="审核日期" width="100" />
|
||||||
|
<el-table-column prop="stockCode" label="存货编码" width="100" />
|
||||||
|
<el-table-column prop="stockName" label="存货名称" width="120" />
|
||||||
|
<el-table-column prop="specModel" label="规格型号" width="100" />
|
||||||
|
<el-table-column label="状态" width="90">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag :type="statusTag(scope.row.orderStatus)" size="mini">{{ statusText(scope.row.orderStatus) }}</el-tag>
|
<el-tag :type="statusTag(scope.row.orderStatus)" size="mini">{{ statusText(scope.row.orderStatus) }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="360" fixed="right">
|
<el-table-column label="操作" width="140" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="text" size="mini" @click="openOrderDialog(scope.row)">编辑</el-button>
|
<el-button type="text" size="mini" @click="openOrderDialog(scope.row)">编辑</el-button>
|
||||||
<el-button type="text" size="mini" @click="handleDeleteOrder(scope.row)" style="color:#c0392b">删除</el-button>
|
<el-button type="text" size="mini" @click="handleDeleteOrder(scope.row)" style="color:#c0392b">删除</el-button>
|
||||||
<el-divider direction="vertical" />
|
<!-- <el-divider direction="vertical" />
|
||||||
<el-button type="text" size="mini" @click="confirmOrder(scope.row)">下达</el-button>
|
<el-button type="text" size="mini" @click="confirmOrder(scope.row)">下达</el-button>
|
||||||
<el-button type="text" size="mini" @click="partialOrder(scope.row)">部分到货</el-button>
|
<el-button type="text" size="mini" @click="partialOrder(scope.row)">部分到货</el-button>
|
||||||
<el-button type="text" size="mini" @click="completeOrder(scope.row)">完成</el-button>
|
<el-button type="text" size="mini" @click="completeOrder(scope.row)">完成</el-button>
|
||||||
<el-button type="text" size="mini" @click="cancelOrder(scope.row)">取消</el-button>
|
<el-button type="text" size="mini" @click="cancelOrder(scope.row)">取消</el-button> -->
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -84,23 +94,30 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- 订单弹窗 -->
|
<!-- 订单弹窗 -->
|
||||||
<el-dialog :title="orderDialog.title" :visible.sync="orderDialog.visible" width="960px" class="order-dialog" @close="closeOrderDialog">
|
<el-dialog :title="orderDialog.title" :visible.sync="orderDialog.visible" width="1200px" class="order-dialog" @close="closeOrderDialog">
|
||||||
<div class="contract-layout">
|
<div class="contract-layout">
|
||||||
<section class="contract-card">
|
<section class="contract-card">
|
||||||
<div class="contract-card__title">基础信息</div>
|
<div class="contract-card__title">基础信息</div>
|
||||||
<el-form :model="orderDialog.form" :rules="orderRules" ref="orderForm" label-width="100px" size="small" class="contract-form">
|
<el-form :model="orderDialog.form" :rules="orderRules" ref="orderForm" label-width="100px" size="small" class="contract-form">
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<el-col :span="12">
|
<el-col :span="8">
|
||||||
<el-form-item label="订单编号" prop="orderCode">
|
<el-form-item label="订单编号" prop="orderCode">
|
||||||
<el-input v-model="orderDialog.form.orderCode" />
|
<el-input v-model="orderDialog.form.orderCode" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="8">
|
||||||
<el-form-item label="订单类型">
|
<el-form-item label="单据类型">
|
||||||
<el-input v-model="orderDialog.form.orderType" placeholder="如标准采购、年度合同" />
|
<el-input v-model="orderDialog.form.billType" placeholder="请输入单据类型" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="8">
|
||||||
|
<el-form-item label="收发类别">
|
||||||
|
<el-input v-model="orderDialog.form.receiveType" placeholder="请输入收发类别" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="8">
|
||||||
<el-form-item label="供应商" prop="supplierId">
|
<el-form-item label="供应商" prop="supplierId">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="orderDialog.form.supplierId"
|
v-model="orderDialog.form.supplierId"
|
||||||
@@ -111,7 +128,6 @@
|
|||||||
:loading="supplierLoading"
|
:loading="supplierLoading"
|
||||||
placeholder="请选择供应商"
|
placeholder="请选择供应商"
|
||||||
@visible-change="handleSupplierDropdown"
|
@visible-change="handleSupplierDropdown"
|
||||||
@change="handleSupplierChange"
|
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="sp in supplierOptions"
|
v-for="sp in supplierOptions"
|
||||||
@@ -122,19 +138,95 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="8">
|
||||||
|
<el-form-item label="部门">
|
||||||
|
<el-input v-model="orderDialog.form.deptName" placeholder="请输入部门" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="业务员">
|
||||||
|
<el-input v-model="orderDialog.form.salesman" placeholder="请输入业务员" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="订单类型">
|
||||||
|
<el-input v-model="orderDialog.form.orderType" placeholder="如标准采购、年度合同" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
<el-form-item label="下单日期" prop="orderDate">
|
<el-form-item label="下单日期" prop="orderDate">
|
||||||
<el-date-picker v-model="orderDialog.form.orderDate" type="date" value-format="yyyy-MM-dd" />
|
<el-date-picker v-model="orderDialog.form.orderDate" type="date" value-format="yyyy-MM-dd" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="8">
|
||||||
<el-form-item label="期望到货">
|
<el-form-item label="期望到货">
|
||||||
<el-date-picker v-model="orderDialog.form.expectedArrival" type="date" value-format="yyyy-MM-dd" />
|
<el-date-picker v-model="orderDialog.form.expectedArrival" type="date" value-format="yyyy-MM-dd" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="制单人">
|
||||||
|
<el-input v-model="orderDialog.form.maker" placeholder="请输入制单人" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="审核人">
|
||||||
|
<el-input v-model="orderDialog.form.auditor" placeholder="请输入审核人" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="记账人">
|
||||||
|
<el-input v-model="orderDialog.form.bookkeeper" placeholder="请输入记账人" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="审核日期">
|
||||||
|
<el-date-picker v-model="orderDialog.form.auditDate" type="date" value-format="yyyy-MM-dd" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
<el-form-item label="总金额">
|
<el-form-item label="总金额">
|
||||||
<el-input-number v-model="orderDialog.form.totalAmount" :min="0" :precision="2" placeholder="可选" />
|
<el-input-number v-model="orderDialog.form.totalAmount" :min="0" :precision="2" placeholder="可选" style="width:100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="存货编码">
|
||||||
|
<el-input v-model="orderDialog.form.stockCode" placeholder="请输入存货编码" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="存货名称">
|
||||||
|
<el-input v-model="orderDialog.form.stockName" placeholder="请输入存货名称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="规格型号">
|
||||||
|
<el-input v-model="orderDialog.form.specModel" placeholder="请输入规格型号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="主计量单位">
|
||||||
|
<el-input v-model="orderDialog.form.mainUnit" placeholder="请输入主计量单位" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="入库数量">
|
||||||
|
<el-input v-model="orderDialog.form.stockQuantity" placeholder="请输入入库数量" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="仓库ID">
|
||||||
|
<WarehouseSelect v-model="orderDialog.form.warehouseId" />
|
||||||
|
<!-- <el-input-number v-model="orderDialog.form.warehouseId" :min="0" placeholder="可选" style="width:100%" /> -->
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -143,126 +235,12 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="contract-card">
|
|
||||||
<div class="contract-card__title">
|
|
||||||
采购明细
|
|
||||||
<span class="subtitle" v-if="orderDialog.form.orderId">(订单号:{{ orderDialog.form.orderCode || orderDialog.form.orderId }})</span>
|
|
||||||
</div>
|
|
||||||
<div class="contract-card__toolbar">
|
|
||||||
<el-select
|
|
||||||
v-model="selectedSupplierGood"
|
|
||||||
placeholder="从供应商价目表快速添加"
|
|
||||||
filterable
|
|
||||||
clearable
|
|
||||||
:disabled="!orderDialog.form.orderId"
|
|
||||||
@focus="ensureSupplierGoods"
|
|
||||||
@change="handleSelectGoods"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="good in supplierGoods"
|
|
||||||
:key="good.materialTypeCode + '_' + (good.specification || '')"
|
|
||||||
:label="formatGoodLabel(good)"
|
|
||||||
:value="good.materialTypeCode"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
size="mini"
|
|
||||||
icon="el-icon-plus"
|
|
||||||
:disabled="!orderDialog.form.orderId"
|
|
||||||
@click="openItemForm(null, { orderId: orderDialog.form.orderId })"
|
|
||||||
>
|
|
||||||
手动新增
|
|
||||||
</el-button>
|
|
||||||
<span class="toolbar-tip" v-if="!orderDialog.form.orderId">请先保存订单,再维护物料明细</span>
|
|
||||||
</div>
|
|
||||||
<el-table
|
|
||||||
v-loading="dialogItemLoading"
|
|
||||||
:data="dialogItems"
|
|
||||||
size="small"
|
|
||||||
border
|
|
||||||
empty-text="暂未添加物料"
|
|
||||||
>
|
|
||||||
<el-table-column type="index" width="50" />
|
|
||||||
<el-table-column prop="materialTypeCode" label="物料类型" />
|
|
||||||
<el-table-column prop="specification" label="规格" />
|
|
||||||
<el-table-column prop="quantity" label="数量" width="120" />
|
|
||||||
<el-table-column prop="unitPrice" label="含税单价" width="120" />
|
|
||||||
<el-table-column label="操作" width="140">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="text" size="mini" @click="openItemForm(scope.row)">编辑</el-button>
|
|
||||||
<el-button type="text" size="mini" class="danger" @click="handleDeleteItem(scope.row)">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
<div slot="footer" class="contract-footer">
|
<div slot="footer" class="contract-footer">
|
||||||
<el-button @click="orderDialog.visible = false">取 消</el-button>
|
<el-button @click="orderDialog.visible = false">取 消</el-button>
|
||||||
<el-button type="primary" @click="submitOrder">保 存</el-button>
|
<el-button type="primary" @click="submitOrder">保 存</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 明细弹窗 -->
|
|
||||||
<el-dialog :title="itemDialog.title" :visible.sync="itemDialog.visible" width="640px">
|
|
||||||
<div class="inline-filter">
|
|
||||||
<el-input v-model="itemQuery.orderId" placeholder="订单ID" size="small" clearable />
|
|
||||||
<div class="filter-actions">
|
|
||||||
<el-button size="small" type="primary" @click="loadItems">查询</el-button>
|
|
||||||
<el-button size="small" @click="resetItemQuery">重置</el-button>
|
|
||||||
<el-button type="primary" size="small" @click="openItemForm()">新增明细</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-table :data="itemList" border size="small" v-loading="itemLoading">
|
|
||||||
<el-table-column prop="orderId" label="订单ID" />
|
|
||||||
<el-table-column prop="materialTypeCode" label="物料类型" />
|
|
||||||
<el-table-column prop="specification" label="规格" />
|
|
||||||
<el-table-column prop="quantity" label="数量" />
|
|
||||||
<el-table-column prop="unitPrice" label="单价" />
|
|
||||||
<el-table-column label="操作" width="150">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="text" size="mini" @click="openItemForm(scope.row)">编辑</el-button>
|
|
||||||
<el-button type="text" size="mini" @click="handleDeleteItem(scope.row)" style="color:#c0392b">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<pagination
|
|
||||||
v-show="itemTotal > 0"
|
|
||||||
:total="itemTotal"
|
|
||||||
:page.sync="itemQuery.pageNum"
|
|
||||||
:limit.sync="itemQuery.pageSize"
|
|
||||||
@pagination="loadItems"
|
|
||||||
/>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 明细编辑 -->
|
|
||||||
<el-dialog :title="itemFormDialog.title" :visible.sync="itemFormDialog.visible" width="520px">
|
|
||||||
<el-form :model="itemFormDialog.form" :rules="itemRules" ref="itemForm" label-width="110px" size="small">
|
|
||||||
<el-form-item label="订单ID" prop="orderId">
|
|
||||||
<el-input v-model="itemFormDialog.form.orderId" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="物料类型" prop="materialTypeCode">
|
|
||||||
<el-input v-model="itemFormDialog.form.materialTypeCode" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="规格">
|
|
||||||
<el-input v-model="itemFormDialog.form.specification" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="采购数量" prop="quantity">
|
|
||||||
<el-input-number v-model="itemFormDialog.form.quantity" :min="0" :precision="3" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="含税单价">
|
|
||||||
<el-input-number v-model="itemFormDialog.form.unitPrice" :min="0" :precision="2" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="备注">
|
|
||||||
<el-input type="textarea" v-model="itemFormDialog.form.remark" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div slot="footer">
|
|
||||||
<el-button @click="itemFormDialog.visible = false">取 消</el-button>
|
|
||||||
<el-button type="primary" @click="submitItem">保 存</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -276,13 +254,9 @@ import {
|
|||||||
partialPurchaseOrder,
|
partialPurchaseOrder,
|
||||||
completePurchaseOrder,
|
completePurchaseOrder,
|
||||||
cancelPurchaseOrder,
|
cancelPurchaseOrder,
|
||||||
listPurchaseOrderItem,
|
listSupplier
|
||||||
addPurchaseOrderItem,
|
|
||||||
updatePurchaseOrderItem,
|
|
||||||
delPurchaseOrderItem,
|
|
||||||
listSupplier,
|
|
||||||
listSupplierPrice
|
|
||||||
} from '@/api/erp/purchase'
|
} from '@/api/erp/purchase'
|
||||||
|
import WarehouseSelect from "@/components/KLPService/WarehouseSelect/index.vue"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ErpPurchaseOrder',
|
name: 'ErpPurchaseOrder',
|
||||||
@@ -298,26 +272,13 @@ export default {
|
|||||||
orderCode: [{ required: true, message: '请输入订单编号', trigger: 'blur' }],
|
orderCode: [{ required: true, message: '请输入订单编号', trigger: 'blur' }],
|
||||||
supplierId: [{ required: true, message: '请选择供应商', trigger: 'change' }]
|
supplierId: [{ required: true, message: '请选择供应商', trigger: 'change' }]
|
||||||
},
|
},
|
||||||
itemDialog: { visible: false, title: '订单明细' },
|
|
||||||
itemQuery: { pageNum: 1, pageSize: 10, orderId: null },
|
|
||||||
itemList: [],
|
|
||||||
itemTotal: 0,
|
|
||||||
itemLoading: false,
|
|
||||||
itemFormDialog: { visible: false, title: '', form: {} },
|
|
||||||
itemRules: {
|
|
||||||
orderId: [{ required: true, message: '请输入订单ID', trigger: 'blur' }],
|
|
||||||
materialTypeCode: [{ required: true, message: '请输入物料类型', trigger: 'blur' }],
|
|
||||||
quantity: [{ required: true, message: '请输入数量', trigger: 'blur' }]
|
|
||||||
},
|
|
||||||
supplierOptions: [],
|
supplierOptions: [],
|
||||||
supplierLoading: false,
|
supplierLoading: false
|
||||||
dialogItems: [],
|
|
||||||
dialogItemLoading: false,
|
|
||||||
supplierGoods: [],
|
|
||||||
goodsLoading: false,
|
|
||||||
selectedSupplierGood: null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
WarehouseSelect,
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadOrders()
|
this.loadOrders()
|
||||||
// 默认加载一批供应商作为下拉初始选项
|
// 默认加载一批供应商作为下拉初始选项
|
||||||
@@ -368,24 +329,36 @@ export default {
|
|||||||
if (row) {
|
if (row) {
|
||||||
this.orderDialog.form = { ...row }
|
this.orderDialog.form = { ...row }
|
||||||
this.orderDialog.title = '编辑采购订单'
|
this.orderDialog.title = '编辑采购订单'
|
||||||
this.loadDialogItems(row.orderId)
|
|
||||||
if (row.supplierId) {
|
|
||||||
this.handleSupplierChange(row.supplierId)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.orderDialog.form = { orderCode: '', supplierId: '', orderDate: '', orderStatus: 0 }
|
this.orderDialog.form = {
|
||||||
|
orderCode: '',
|
||||||
|
supplierId: '',
|
||||||
|
orderDate: '',
|
||||||
|
orderStatus: 0,
|
||||||
|
billType: '',
|
||||||
|
receiveType: '',
|
||||||
|
deptName: '',
|
||||||
|
salesman: '',
|
||||||
|
maker: '',
|
||||||
|
auditor: '',
|
||||||
|
bookkeeper: '',
|
||||||
|
auditDate: '',
|
||||||
|
totalAmount: 0,
|
||||||
|
supplyUnitId: null,
|
||||||
|
stockCode: '',
|
||||||
|
stockName: '',
|
||||||
|
specModel: '',
|
||||||
|
mainUnit: '',
|
||||||
|
stockQuantity: '',
|
||||||
|
warehouseId: null,
|
||||||
|
remark: ''
|
||||||
|
}
|
||||||
this.orderDialog.title = '新增采购订单'
|
this.orderDialog.title = '新增采购订单'
|
||||||
this.dialogItems = []
|
|
||||||
this.supplierGoods = []
|
|
||||||
this.selectedSupplierGood = null
|
|
||||||
}
|
}
|
||||||
this.orderDialog.visible = true
|
this.orderDialog.visible = true
|
||||||
this.$nextTick(() => this.$refs.orderForm && this.$refs.orderForm.clearValidate())
|
this.$nextTick(() => this.$refs.orderForm && this.$refs.orderForm.clearValidate())
|
||||||
},
|
},
|
||||||
closeOrderDialog() {
|
closeOrderDialog() {
|
||||||
this.dialogItems = []
|
|
||||||
this.supplierGoods = []
|
|
||||||
this.selectedSupplierGood = null
|
|
||||||
},
|
},
|
||||||
submitOrder() {
|
submitOrder() {
|
||||||
this.$refs.orderForm.validate(valid => {
|
this.$refs.orderForm.validate(valid => {
|
||||||
@@ -394,7 +367,6 @@ export default {
|
|||||||
api(this.orderDialog.form).then(() => {
|
api(this.orderDialog.form).then(() => {
|
||||||
this.$message.success('保存成功')
|
this.$message.success('保存成功')
|
||||||
this.orderDialog.visible = false
|
this.orderDialog.visible = false
|
||||||
this.closeOrderDialog()
|
|
||||||
this.loadOrders()
|
this.loadOrders()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -450,134 +422,6 @@ export default {
|
|||||||
4: 'danger'
|
4: 'danger'
|
||||||
}
|
}
|
||||||
return map[status] || 'info'
|
return map[status] || 'info'
|
||||||
},
|
|
||||||
openItemDialog() {
|
|
||||||
this.itemDialog.visible = true
|
|
||||||
this.loadItems()
|
|
||||||
},
|
|
||||||
loadItems() {
|
|
||||||
this.itemLoading = true
|
|
||||||
listPurchaseOrderItem(this.itemQuery)
|
|
||||||
.then(res => {
|
|
||||||
this.itemList = res.rows || []
|
|
||||||
this.itemTotal = res.total || 0
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.itemLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
resetItemQuery() {
|
|
||||||
this.itemQuery = { pageNum: 1, pageSize: 10, orderId: null }
|
|
||||||
this.loadItems()
|
|
||||||
},
|
|
||||||
openItemForm(row, preset = {}) {
|
|
||||||
if (row) {
|
|
||||||
this.itemFormDialog.form = { ...row }
|
|
||||||
this.itemFormDialog.title = '编辑明细'
|
|
||||||
} else {
|
|
||||||
const targetOrderId = preset.orderId || this.orderDialog.form.orderId || ''
|
|
||||||
if (!targetOrderId) {
|
|
||||||
this.$message.warning('请先保存订单,再新增物料')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.itemFormDialog.form = {
|
|
||||||
orderId: targetOrderId,
|
|
||||||
materialTypeCode: preset.materialTypeCode || '',
|
|
||||||
specification: preset.specification || '',
|
|
||||||
quantity: preset.quantity || 0,
|
|
||||||
unitPrice: preset.unitPrice || 0,
|
|
||||||
remark: preset.remark || ''
|
|
||||||
}
|
|
||||||
this.itemFormDialog.title = '新增明细'
|
|
||||||
}
|
|
||||||
this.itemFormDialog.visible = true
|
|
||||||
this.$nextTick(() => this.$refs.itemForm && this.$refs.itemForm.clearValidate())
|
|
||||||
},
|
|
||||||
submitItem() {
|
|
||||||
this.$refs.itemForm.validate(valid => {
|
|
||||||
if (!valid) return
|
|
||||||
const api = this.itemFormDialog.form.itemId ? updatePurchaseOrderItem : addPurchaseOrderItem
|
|
||||||
api(this.itemFormDialog.form).then(() => {
|
|
||||||
this.$message.success('保存成功')
|
|
||||||
this.itemFormDialog.visible = false
|
|
||||||
this.loadItems()
|
|
||||||
if (this.orderDialog.form.orderId) {
|
|
||||||
this.loadDialogItems(this.orderDialog.form.orderId)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleDeleteItem(row) {
|
|
||||||
this.$confirm('确定删除该明细吗?', '提示').then(() => {
|
|
||||||
return delPurchaseOrderItem(row.itemId)
|
|
||||||
}).then(() => {
|
|
||||||
this.$message.success('删除成功')
|
|
||||||
this.loadItems()
|
|
||||||
if (this.orderDialog.form.orderId) {
|
|
||||||
this.loadDialogItems(this.orderDialog.form.orderId)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
async loadDialogItems(orderId) {
|
|
||||||
if (!orderId) {
|
|
||||||
this.dialogItems = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.dialogItemLoading = true
|
|
||||||
try {
|
|
||||||
const res = await listPurchaseOrderItem({ pageNum: 1, pageSize: 100, orderId })
|
|
||||||
this.dialogItems = res.rows || []
|
|
||||||
} finally {
|
|
||||||
this.dialogItemLoading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleSupplierChange(val) {
|
|
||||||
if (!val) {
|
|
||||||
this.supplierGoods = []
|
|
||||||
this.selectedSupplierGood = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.selectedSupplierGood = null
|
|
||||||
this.loadSupplierGoods(val)
|
|
||||||
},
|
|
||||||
ensureSupplierGoods() {
|
|
||||||
if (!this.orderDialog.form.supplierId || this.supplierGoods.length || this.goodsLoading) return
|
|
||||||
this.loadSupplierGoods(this.orderDialog.form.supplierId)
|
|
||||||
},
|
|
||||||
async loadSupplierGoods(supplierId) {
|
|
||||||
if (!supplierId) return
|
|
||||||
this.goodsLoading = true
|
|
||||||
try {
|
|
||||||
const res = await listSupplierPrice({ pageNum: 1, pageSize: 50, supplierId })
|
|
||||||
this.supplierGoods = res.rows || []
|
|
||||||
} finally {
|
|
||||||
this.goodsLoading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleSelectGoods(code) {
|
|
||||||
if (!code) return
|
|
||||||
if (!this.orderDialog.form.orderId) {
|
|
||||||
this.$message.warning('请先保存订单,再添加物料')
|
|
||||||
this.selectedSupplierGood = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const good = this.supplierGoods.find(item => item.materialTypeCode === code)
|
|
||||||
if (!good) return
|
|
||||||
this.openItemForm(null, {
|
|
||||||
orderId: this.orderDialog.form.orderId,
|
|
||||||
materialTypeCode: good.materialTypeCode,
|
|
||||||
specification: good.specification,
|
|
||||||
unitPrice: good.price || 0
|
|
||||||
})
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.selectedSupplierGood = null
|
|
||||||
})
|
|
||||||
},
|
|
||||||
formatGoodLabel(good) {
|
|
||||||
if (!good) return ''
|
|
||||||
const spec = good.specification ? ` / ${good.specification}` : ''
|
|
||||||
const price = good.price != null ? ` · ¥${good.price}` : ''
|
|
||||||
return `${good.materialTypeCode}${spec}${price}`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -619,11 +463,8 @@ export default {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.danger {
|
|
||||||
color: #c0392b;
|
|
||||||
}
|
|
||||||
.order-dialog ::v-deep .el-dialog {
|
.order-dialog ::v-deep .el-dialog {
|
||||||
max-width: 960px;
|
max-width: 1200px;
|
||||||
}
|
}
|
||||||
.contract-layout {
|
.contract-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -641,26 +482,6 @@ export default {
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: #1f2d3d;
|
color: #1f2d3d;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
.subtitle {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #9099a8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.contract-card__toolbar {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
.toolbar-tip {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #c97a1a;
|
|
||||||
}
|
|
||||||
.el-select {
|
|
||||||
max-width: 320px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.contract-footer {
|
.contract-footer {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|||||||
@@ -1,765 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="erp-purchase-page">
|
|
||||||
<section class="surface-panel metrics-panel">
|
|
||||||
<div class="metrics-grid">
|
|
||||||
<div class="metric">
|
|
||||||
<p class="label">采购总金额</p>
|
|
||||||
<p class="value">¥{{ summary.totalAmount | formatAmount }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="metric">
|
|
||||||
<p class="label">计划采购量</p>
|
|
||||||
<p class="value">{{ summary.suggestionTotal }} 吨</p>
|
|
||||||
</div>
|
|
||||||
<div class="metric">
|
|
||||||
<p class="label">待处理订单</p>
|
|
||||||
<p class="value">{{ summary.pendingOrder }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="metric">
|
|
||||||
<p class="label">供应商数量</p>
|
|
||||||
<p class="value">{{ summary.supplierBrief.length }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="surface-panel">
|
|
||||||
<header class="surface-header">
|
|
||||||
<span>采购需求分析</span>
|
|
||||||
<div class="surface-actions">
|
|
||||||
<el-switch v-model="persistResult" active-text="写入建议表" inactive-text="仅计算" />
|
|
||||||
<el-button type="primary" size="small" @click="handleAnalyze" :loading="analysisLoading">执行分析</el-button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<el-table :data="mappingRows" border size="small" class="mapping-table">
|
|
||||||
<el-table-column label="产品">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<!-- <el-input v-model="scope.row.productId" placeholder="产品ID" /> -->
|
|
||||||
<ProductSelect v-model="scope.row.productId" placeholder="选择产品" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="原料">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<!-- <el-input v-model="scope.row.rawMaterialId" placeholder="原料ID" /> -->
|
|
||||||
<RawMaterialSelect v-model="scope.row.rawMaterialId" placeholder="选择原料" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="转换率">
|
|
||||||
<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="removeMappingRow(scope.$index)" :disabled="mappingRows.length === 1">移除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<div class="mapping-toolbar">
|
|
||||||
<el-button icon="el-icon-plus" size="mini" @click="addMappingRow">新增映射</el-button>
|
|
||||||
</div>
|
|
||||||
<el-table
|
|
||||||
:data="analysisData"
|
|
||||||
v-loading="analysisLoading"
|
|
||||||
size="small"
|
|
||||||
border
|
|
||||||
class="analysis-table"
|
|
||||||
show-summary
|
|
||||||
:summary-method="analysisSummary"
|
|
||||||
>
|
|
||||||
<el-table-column label="产品" prop="productName">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<div class="cell-title">{{ scope.row.productName || '-' }}</div>
|
|
||||||
<div class="cell-sub">ID: {{ scope.row.productId || '-' }}</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="规格" prop="specification" />
|
|
||||||
<el-table-column label="销售需求(吨)" prop="salesDemand" />
|
|
||||||
<el-table-column label="成品库存/卷">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<div>{{ scope.row.productStockWeight }}</div>
|
|
||||||
<div class="cell-sub">{{ scope.row.productStockCoilCount }} 卷</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="原料折算(吨)" prop="rawStockConverted" />
|
|
||||||
<el-table-column label="在途折算(吨)" prop="inTransitConverted" />
|
|
||||||
<el-table-column label="待下达折算(吨)" prop="pendingConverted" />
|
|
||||||
<el-table-column label="建议采购(吨)" prop="suggestedPurchase" />
|
|
||||||
</el-table>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="surface-panel">
|
|
||||||
<header class="surface-header">
|
|
||||||
<span>执行控制</span>
|
|
||||||
</header>
|
|
||||||
<el-tabs v-model="activeTab" class="plain-tabs">
|
|
||||||
<el-tab-pane label="收货记录" name="receipt">
|
|
||||||
<div class="inline-filter">
|
|
||||||
<!-- <el-input v-model="receiptQuery.orderId" placeholder="订单ID" size="small" clearable /> -->
|
|
||||||
<OrderSelect v-model="receiptQuery.orderId" size="small" clearable />
|
|
||||||
<el-input v-model="receiptQuery.itemId" placeholder="明细ID" size="small" clearable />
|
|
||||||
<div class="filter-actions">
|
|
||||||
<el-button size="small" type="primary" @click="loadReceipts">查询</el-button>
|
|
||||||
<el-button size="small" @click="resetReceiptQuery">重置</el-button>
|
|
||||||
<el-button type="primary" size="small" @click="openReceiptDialog()">新增收货</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-table :data="receiptList" v-loading="receiptLoading" border size="small">
|
|
||||||
<el-table-column prop="receiptId" label="ID" width="80" />
|
|
||||||
<el-table-column prop="orderId" label="订单ID" />
|
|
||||||
<el-table-column prop="itemId" label="明细ID" />
|
|
||||||
<el-table-column prop="receivedQty" label="收货数量" />
|
|
||||||
<el-table-column prop="qualityResult" label="质检结果">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-tag :type="scope.row.qualityResult === 'NG' ? 'danger' : 'success'" size="mini">
|
|
||||||
{{ scope.row.qualityResult || '合格' }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="receiptTime" label="收货时间" />
|
|
||||||
<el-table-column label="操作" width="160">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="text" size="mini" @click="openReceiptDialog(scope.row)">编辑</el-button>
|
|
||||||
<el-button type="text" size="mini" @click="handleDeleteReceipt(scope.row)">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<pagination
|
|
||||||
v-show="receiptTotal > 0"
|
|
||||||
:total="receiptTotal"
|
|
||||||
:page.sync="receiptQuery.pageNum"
|
|
||||||
:limit.sync="receiptQuery.pageSize"
|
|
||||||
@pagination="loadReceipts"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="退货管理" name="return">
|
|
||||||
<div class="inline-filter">
|
|
||||||
<!-- <el-input v-model="returnQuery.orderId" placeholder="订单ID" size="small" clearable /> -->
|
|
||||||
<OrderSelect v-model="returnQuery.orderId" placeholder="选择订单" size="small" clearable />
|
|
||||||
<el-select v-model="returnQuery.status" placeholder="状态" size="small" clearable>
|
|
||||||
<el-option label="草稿" :value="0" />
|
|
||||||
<el-option label="完成" :value="1" />
|
|
||||||
</el-select>
|
|
||||||
<div class="filter-actions">
|
|
||||||
<el-button size="small" type="primary" @click="loadReturns">查询</el-button>
|
|
||||||
<el-button size="small" @click="resetReturnQuery">重置</el-button>
|
|
||||||
<el-button type="primary" size="small" @click="openReturnDialog()">新增退货</el-button>
|
|
||||||
<el-button size="small" @click="openReturnItemDialog()">退货明细</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-table :data="returnList" v-loading="returnLoading" border size="small">
|
|
||||||
<el-table-column prop="returnId" label="退货单ID" width="120" />
|
|
||||||
<el-table-column prop="orderId" label="订单ID" />
|
|
||||||
<el-table-column prop="returnType" label="类型" />
|
|
||||||
<el-table-column prop="reason" label="原因" />
|
|
||||||
<el-table-column prop="status" label="状态" width="140">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'" size="mini">
|
|
||||||
{{ scope.row.status === 1 ? '完成' : '草稿' }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="160">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="text" size="mini" @click="openReturnDialog(scope.row)">编辑</el-button>
|
|
||||||
<el-button type="text" size="mini" @click="handleDeleteReturn(scope.row)">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<pagination
|
|
||||||
v-show="returnTotal > 0"
|
|
||||||
:total="returnTotal"
|
|
||||||
:page.sync="returnQuery.pageNum"
|
|
||||||
:limit.sync="returnQuery.pageSize"
|
|
||||||
@pagination="loadReturns"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="surface-panel">
|
|
||||||
<header class="surface-header">
|
|
||||||
<span>采购报表</span>
|
|
||||||
<div class="surface-actions">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="reportRange"
|
|
||||||
type="monthrange"
|
|
||||||
unlink-panels
|
|
||||||
value-format="yyyy-MM"
|
|
||||||
range-separator="至"
|
|
||||||
start-placeholder="开始月份"
|
|
||||||
end-placeholder="结束月份"
|
|
||||||
@change="loadReports"
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div class="report-grid">
|
|
||||||
<div class="report-card">
|
|
||||||
<h4>供应商采购额TOP</h4>
|
|
||||||
<el-table :data="summary.supplierBrief" size="mini" height="220" border>
|
|
||||||
<el-table-column prop="supplierId" label="供应商ID" />
|
|
||||||
<el-table-column prop="totalAmount" label="金额" />
|
|
||||||
<el-table-column prop="orderCount" label="订单" width="90" />
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
<div class="report-card">
|
|
||||||
<h4>价格趋势</h4>
|
|
||||||
<el-table :data="priceTrend" size="mini" height="220" border>
|
|
||||||
<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" size="mini" height="220" border>
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<!-- 收货弹窗 -->
|
|
||||||
<el-dialog :title="receiptDialog.title" :visible.sync="receiptDialog.visible" width="480px">
|
|
||||||
<el-form :model="receiptDialog.form" :rules="receiptRules" ref="receiptForm" label-width="100px" size="small">
|
|
||||||
<el-form-item label="订单ID" prop="orderId">
|
|
||||||
<!-- <el-input v-model="receiptDialog.form.orderId" /> -->
|
|
||||||
<OrderSelect v-model="receiptDialog.form.orderId" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="明细ID" prop="itemId">
|
|
||||||
<!-- <el-input v-model="receiptDialog.form.itemId" /> -->
|
|
||||||
<OrderDetailSelect v-model="receiptDialog.form.itemId" :orderId="receiptDialog.form.orderId" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="收货数量" prop="receivedQty">
|
|
||||||
<el-input-number v-model="receiptDialog.form.receivedQty" :min="0" :precision="3" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="质检结果" prop="qualityResult">
|
|
||||||
<el-select v-model="receiptDialog.form.qualityResult" clearable placeholder="默认合格">
|
|
||||||
<el-option label="合格" value="OK" />
|
|
||||||
<el-option label="不合格" value="NG" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="收货时间">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="receiptDialog.form.receiptTime"
|
|
||||||
type="datetime"
|
|
||||||
value-format="yyyy-MM-dd HH:mm:ss"
|
|
||||||
placeholder="自动取当前"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="备注">
|
|
||||||
<el-input type="textarea" v-model="receiptDialog.form.remark" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div slot="footer">
|
|
||||||
<el-button @click="receiptDialog.visible = false">取 消</el-button>
|
|
||||||
<el-button type="primary" @click="submitReceipt">保 存</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 退货弹窗 -->
|
|
||||||
<el-dialog :title="returnDialog.title" :visible.sync="returnDialog.visible" width="600px">
|
|
||||||
<el-form :model="returnDialog.form" :rules="returnRules" ref="returnForm" label-width="100px" size="small">
|
|
||||||
<el-form-item label="订单ID" prop="orderId">
|
|
||||||
<OrderSelect v-model="returnDialog.form.orderId" placeholder="选择订单" size="small" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="退货类型">
|
|
||||||
<el-select v-model="returnDialog.form.returnType" clearable>
|
|
||||||
<el-option label="质量问题" value="QUALITY" />
|
|
||||||
<el-option label="数量错误" value="QTY" />
|
|
||||||
<el-option label="规格不符" value="SPEC" />
|
|
||||||
<el-option label="其他" value="OTHER" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="退货原因">
|
|
||||||
<el-input type="textarea" v-model="returnDialog.form.reason" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="状态">
|
|
||||||
<el-select v-model="returnDialog.form.status">
|
|
||||||
<el-option label="草稿" :value="0" />
|
|
||||||
<el-option label="完成" :value="1" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="备注">
|
|
||||||
<el-input type="textarea" v-model="returnDialog.form.remark" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div slot="footer">
|
|
||||||
<el-button @click="returnDialog.visible = false">取 消</el-button>
|
|
||||||
<el-button type="primary" @click="submitReturn">保 存</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 退货明细弹窗 -->
|
|
||||||
<el-dialog :title="returnItemDialog.title" :visible.sync="returnItemDialog.visible" width="600px">
|
|
||||||
<div class="toolbar">
|
|
||||||
<el-input v-model="returnItemQuery.returnId" placeholder="退货单ID" size="small" clearable class="toolbar-input" />
|
|
||||||
<OrderSelect v-model="returnItemQuery.orderId" />
|
|
||||||
<OrderDetailSelect v-model="returnItemQuery.itemId" :orderId="returnItemQuery.orderId" placeholder="选择订单明细" size="small" clearable />
|
|
||||||
<el-button size="small" type="primary" @click="loadReturnItems">查询</el-button>
|
|
||||||
<el-button size="small" @click="resetReturnItemQuery">重置</el-button>
|
|
||||||
<div class="toolbar-spacer"></div>
|
|
||||||
<el-button type="primary" size="small" @click="openReturnItemForm()">新增明细</el-button>
|
|
||||||
</div>
|
|
||||||
<el-table :data="returnItemList" v-loading="returnItemLoading" border size="small">
|
|
||||||
<el-table-column prop="returnItemId" label="ID" width="80" />
|
|
||||||
<el-table-column prop="returnId" label="退货单ID" width="100" />
|
|
||||||
<el-table-column prop="itemId" label="订单明细ID" width="120" />
|
|
||||||
<el-table-column prop="returnQty" label="退货数量" width="120" />
|
|
||||||
<el-table-column label="操作" width="140">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="text" size="mini" @click="openReturnItemForm(scope.row)">编辑</el-button>
|
|
||||||
<el-button type="text" size="mini" @click="handleDeleteReturnItem(scope.row)">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<pagination
|
|
||||||
v-show="returnItemTotal > 0"
|
|
||||||
:total="returnItemTotal"
|
|
||||||
:page.sync="returnItemQuery.pageNum"
|
|
||||||
:limit.sync="returnItemQuery.pageSize"
|
|
||||||
@pagination="loadReturnItems"
|
|
||||||
/>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 退货明细编辑 -->
|
|
||||||
<el-dialog :title="returnItemFormDialog.title" :visible.sync="returnItemFormDialog.visible" width="420px">
|
|
||||||
<el-form ref="returnItemForm" :model="returnItemFormDialog.form" :rules="returnItemRules" label-width="110px" size="small">
|
|
||||||
<el-form-item label="退货单ID" prop="returnId">
|
|
||||||
<el-input v-model="returnItemFormDialog.form.returnId" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="订单ID" prop="orderId">
|
|
||||||
<OrderSelect v-model="returnItemFormDialog.form.orderId" placeholder="选择订单" size="small" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="订单明细ID" prop="itemId">
|
|
||||||
<OrderDetailSelect v-model="returnItemFormDialog.form.itemId" :orderId="returnItemFormDialog.form.orderId" placeholder="选择订单明细" size="small" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="退货数量" prop="returnQty">
|
|
||||||
<el-input-number v-model="returnItemFormDialog.form.returnQty" :min="0" :precision="3" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="照片">
|
|
||||||
<el-input v-model="returnItemFormDialog.form.photos" placeholder="图片URL,用逗号分隔" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="备注">
|
|
||||||
<el-input type="textarea" v-model="returnItemFormDialog.form.remark" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div slot="footer">
|
|
||||||
<el-button @click="returnItemFormDialog.visible = false">取 消</el-button>
|
|
||||||
<el-button type="primary" @click="submitReturnItem">保 存</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import {
|
|
||||||
analyzePurchaseRequirement,
|
|
||||||
listPurchaseReceipt,
|
|
||||||
addPurchaseReceipt,
|
|
||||||
updatePurchaseReceipt,
|
|
||||||
delPurchaseReceipt,
|
|
||||||
listPurchaseReturn,
|
|
||||||
addPurchaseReturn,
|
|
||||||
updatePurchaseReturn,
|
|
||||||
delPurchaseReturn,
|
|
||||||
listPurchaseReturnItem,
|
|
||||||
addPurchaseReturnItem,
|
|
||||||
updatePurchaseReturnItem,
|
|
||||||
delPurchaseReturnItem,
|
|
||||||
getPurchaseReportSummary,
|
|
||||||
getPurchasePriceTrend,
|
|
||||||
getSupplierQuality
|
|
||||||
} from '@/api/erp/purchase'
|
|
||||||
import RawMaterialSelect from '@/components/KLPService/RawMaterialSelect'
|
|
||||||
import ProductSelect from '@/components/KLPService/ProductSelect'
|
|
||||||
import OrderSelect from '@/components/KLPService/OrderSelect'
|
|
||||||
import OrderDetailSelect from '@/components/KLPService/OrderDetailSelect'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ErpPurchaseWorkbench',
|
|
||||||
components: {
|
|
||||||
RawMaterialSelect,
|
|
||||||
ProductSelect,
|
|
||||||
OrderSelect,
|
|
||||||
OrderDetailSelect,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
mappingRows: [{ productId: '', rawMaterialId: '', conversionRate: 1 }],
|
|
||||||
persistResult: false,
|
|
||||||
analysisLoading: false,
|
|
||||||
analysisData: [],
|
|
||||||
summary: { totalAmount: 0, suggestionTotal: 0, pendingOrder: 0, supplierBrief: [] },
|
|
||||||
reportRange: [],
|
|
||||||
priceTrend: [],
|
|
||||||
supplierQuality: [],
|
|
||||||
activeTab: 'receipt',
|
|
||||||
receiptList: [],
|
|
||||||
receiptTotal: 0,
|
|
||||||
receiptLoading: false,
|
|
||||||
receiptQuery: { pageNum: 1, pageSize: 10, orderId: null, itemId: null },
|
|
||||||
receiptDialog: { visible: false, title: '', form: {} },
|
|
||||||
receiptRules: {
|
|
||||||
orderId: [{ required: true, message: '请输入订单ID', trigger: 'blur' }],
|
|
||||||
itemId: [{ required: true, message: '请输入明细ID', trigger: 'blur' }],
|
|
||||||
receivedQty: [{ required: true, message: '请输入收货数量', trigger: 'blur' }]
|
|
||||||
},
|
|
||||||
returnList: [],
|
|
||||||
returnTotal: 0,
|
|
||||||
returnLoading: false,
|
|
||||||
returnQuery: { pageNum: 1, pageSize: 10, orderId: null, status: null },
|
|
||||||
returnDialog: { visible: false, title: '', form: {} },
|
|
||||||
returnRules: {
|
|
||||||
orderId: [{ required: true, message: '请输入订单ID', trigger: 'blur' }]
|
|
||||||
},
|
|
||||||
returnItemDialog: { visible: false, title: '退货明细列表' },
|
|
||||||
returnItemQuery: { pageNum: 1, pageSize: 10, returnId: null, itemId: null },
|
|
||||||
returnItemList: [],
|
|
||||||
returnItemTotal: 0,
|
|
||||||
returnItemLoading: false,
|
|
||||||
returnItemFormDialog: { visible: false, title: '', form: {} },
|
|
||||||
returnItemRules: {
|
|
||||||
returnId: [{ required: true, message: '请输入退货单ID', trigger: 'blur' }],
|
|
||||||
itemId: [{ required: true, message: '请输入订单明细ID', trigger: 'blur' }],
|
|
||||||
returnQty: [{ required: true, message: '请输入退货数量', trigger: 'blur' }]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
formatAmount(val) {
|
|
||||||
if (!val) return '0.00'
|
|
||||||
return Number(val).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.loadReports()
|
|
||||||
this.loadReceipts()
|
|
||||||
this.loadReturns()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
addMappingRow() {
|
|
||||||
this.mappingRows.push({ productId: '', rawMaterialId: '', conversionRate: 1 })
|
|
||||||
},
|
|
||||||
removeMappingRow(index) {
|
|
||||||
if (this.mappingRows.length === 1) return
|
|
||||||
this.mappingRows.splice(index, 1)
|
|
||||||
},
|
|
||||||
handleAnalyze() {
|
|
||||||
if (!this.mappingRows.every(row => row.productId && row.rawMaterialId)) {
|
|
||||||
this.$message.warning('请补全产品与原料映射')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.analysisLoading = true
|
|
||||||
const payload = {
|
|
||||||
mappings: this.mappingRows.map(row => ({
|
|
||||||
productId: Number(row.productId),
|
|
||||||
rawMaterialId: Number(row.rawMaterialId),
|
|
||||||
conversionRate: Number(row.conversionRate || 0)
|
|
||||||
})),
|
|
||||||
persistResult: this.persistResult
|
|
||||||
}
|
|
||||||
analyzePurchaseRequirement(payload)
|
|
||||||
.then(res => {
|
|
||||||
this.analysisData = res || []
|
|
||||||
this.summary.suggestionTotal = this.analysisData.reduce((sum, item) => sum + Number(item.suggestedPurchase || 0), 0)
|
|
||||||
this.$message.success('分析完成')
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.analysisLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
analysisSummary({ data }) {
|
|
||||||
const sums = []
|
|
||||||
const columns = ['salesDemand', 'productStockWeight', 'rawStockConverted', 'inTransitConverted', 'pendingConverted', 'suggestedPurchase']
|
|
||||||
sums[0] = '总计'
|
|
||||||
data.forEach(row => {
|
|
||||||
columns.forEach((col, idx) => {
|
|
||||||
const value = Number(row[col] || 0)
|
|
||||||
sums[idx + 2] = (Number(sums[idx + 2]) || 0) + value
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return sums
|
|
||||||
},
|
|
||||||
loadReports() {
|
|
||||||
const params = {}
|
|
||||||
if (this.reportRange && this.reportRange.length === 2) {
|
|
||||||
params.beginTime = `${this.reportRange[0]}-01`
|
|
||||||
params.endTime = `${this.reportRange[1]}-31`
|
|
||||||
}
|
|
||||||
getPurchaseReportSummary(params).then(res => {
|
|
||||||
this.summary.totalAmount = res.totalAmount || 0
|
|
||||||
this.summary.supplierBrief = res.bySupplier || []
|
|
||||||
this.summary.pendingOrder = this.summary.supplierBrief.reduce((sum, item) => sum + (item.orderCount || 0), 0)
|
|
||||||
})
|
|
||||||
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)}%`
|
|
||||||
},
|
|
||||||
// 收货
|
|
||||||
loadReceipts() {
|
|
||||||
this.receiptLoading = true
|
|
||||||
listPurchaseReceipt(this.receiptQuery)
|
|
||||||
.then(res => {
|
|
||||||
this.receiptList = res.rows || []
|
|
||||||
this.receiptTotal = res.total || 0
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.receiptLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
resetReceiptQuery() {
|
|
||||||
this.receiptQuery = { pageNum: 1, pageSize: 10, orderId: null, itemId: null }
|
|
||||||
this.loadReceipts()
|
|
||||||
},
|
|
||||||
openReceiptDialog(row) {
|
|
||||||
if (row) {
|
|
||||||
this.receiptDialog.form = { ...row }
|
|
||||||
this.receiptDialog.title = '编辑收货'
|
|
||||||
} else {
|
|
||||||
this.receiptDialog.form = { orderId: '', itemId: '', receivedQty: 0, qualityResult: 'OK' }
|
|
||||||
this.receiptDialog.title = '新增收货'
|
|
||||||
}
|
|
||||||
this.receiptDialog.visible = true
|
|
||||||
this.$nextTick(() => this.$refs.receiptForm && this.$refs.receiptForm.clearValidate())
|
|
||||||
},
|
|
||||||
submitReceipt() {
|
|
||||||
this.$refs.receiptForm.validate(valid => {
|
|
||||||
if (!valid) return
|
|
||||||
const api = this.receiptDialog.form.receiptId ? updatePurchaseReceipt : addPurchaseReceipt
|
|
||||||
api(this.receiptDialog.form).then(() => {
|
|
||||||
this.$message.success('保存成功')
|
|
||||||
this.receiptDialog.visible = false
|
|
||||||
this.loadReceipts()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleDeleteReceipt(row) {
|
|
||||||
this.$confirm('确定删除该收货记录吗?', '提示').then(() => {
|
|
||||||
return delPurchaseReceipt(row.receiptId)
|
|
||||||
}).then(() => {
|
|
||||||
this.$message.success('删除成功')
|
|
||||||
this.loadReceipts()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 退货
|
|
||||||
loadReturns() {
|
|
||||||
this.returnLoading = true
|
|
||||||
listPurchaseReturn(this.returnQuery)
|
|
||||||
.then(res => {
|
|
||||||
this.returnList = res.rows || []
|
|
||||||
this.returnTotal = res.total || 0
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.returnLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
resetReturnQuery() {
|
|
||||||
this.returnQuery = { pageNum: 1, pageSize: 10, orderId: null, status: null }
|
|
||||||
this.loadReturns()
|
|
||||||
},
|
|
||||||
openReturnDialog(row) {
|
|
||||||
if (row) {
|
|
||||||
this.returnDialog.form = { ...row }
|
|
||||||
this.returnDialog.title = '编辑退货单'
|
|
||||||
} else {
|
|
||||||
this.returnDialog.form = { orderId: '', returnType: 'QUALITY', status: 0 }
|
|
||||||
this.returnDialog.title = '新增退货单'
|
|
||||||
}
|
|
||||||
this.returnDialog.visible = true
|
|
||||||
this.$nextTick(() => this.$refs.returnForm && this.$refs.returnForm.clearValidate())
|
|
||||||
},
|
|
||||||
submitReturn() {
|
|
||||||
this.$refs.returnForm.validate(valid => {
|
|
||||||
if (!valid) return
|
|
||||||
const api = this.returnDialog.form.returnId ? updatePurchaseReturn : addPurchaseReturn
|
|
||||||
api(this.returnDialog.form).then(() => {
|
|
||||||
this.$message.success('保存成功')
|
|
||||||
this.returnDialog.visible = false
|
|
||||||
this.loadReturns()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleDeleteReturn(row) {
|
|
||||||
this.$confirm('确定删除该退货单吗?', '提示').then(() => {
|
|
||||||
return delPurchaseReturn(row.returnId)
|
|
||||||
}).then(() => {
|
|
||||||
this.$message.success('删除成功')
|
|
||||||
this.loadReturns()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 退货明细
|
|
||||||
openReturnItemDialog() {
|
|
||||||
this.returnItemDialog.visible = true
|
|
||||||
|
|
||||||
this.loadReturnItems()
|
|
||||||
},
|
|
||||||
loadReturnItems() {
|
|
||||||
this.returnItemLoading = true
|
|
||||||
listPurchaseReturnItem(this.returnItemQuery)
|
|
||||||
.then(res => {
|
|
||||||
this.returnItemList = res.rows || []
|
|
||||||
this.returnItemTotal = res.total || 0
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.returnItemLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
resetReturnItemQuery() {
|
|
||||||
this.returnItemQuery = { pageNum: 1, pageSize: 10, returnId: null, itemId: null }
|
|
||||||
this.loadReturnItems()
|
|
||||||
},
|
|
||||||
openReturnItemForm(row) {
|
|
||||||
if (row) {
|
|
||||||
this.returnItemFormDialog.form = { ...row }
|
|
||||||
this.returnItemFormDialog.title = '编辑退货明细'
|
|
||||||
} else {
|
|
||||||
this.returnItemFormDialog.form = { returnId: '', itemId: '', returnQty: 0, orderId: row.orderId }
|
|
||||||
this.returnItemFormDialog.title = '新增退货明细'
|
|
||||||
}
|
|
||||||
this.returnItemFormDialog.visible = true
|
|
||||||
this.$nextTick(() => this.$refs.returnItemForm && this.$refs.returnItemForm.clearValidate())
|
|
||||||
},
|
|
||||||
submitReturnItem() {
|
|
||||||
this.$refs.returnItemForm.validate(valid => {
|
|
||||||
if (!valid) return
|
|
||||||
const api = this.returnItemFormDialog.form.returnItemId ? updatePurchaseReturnItem : addPurchaseReturnItem
|
|
||||||
api(this.returnItemFormDialog.form).then(() => {
|
|
||||||
this.$message.success('保存成功')
|
|
||||||
this.returnItemFormDialog.visible = false
|
|
||||||
this.loadReturnItems()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleDeleteReturnItem(row) {
|
|
||||||
this.$confirm('确定删除该退货明细吗?', '提示').then(() => {
|
|
||||||
return delPurchaseReturnItem(row.returnItemId)
|
|
||||||
}).then(() => {
|
|
||||||
this.$message.success('删除成功')
|
|
||||||
this.loadReturnItems()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.erp-purchase-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: #1c2b36;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.surface-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
.metrics-panel {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.metrics-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
||||||
gap: 12px;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
.metric {
|
|
||||||
border: 1px solid #d6dce1;
|
|
||||||
padding: 12px;
|
|
||||||
background: #f9fafb;
|
|
||||||
.label {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #6b7785;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
.value {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #16212b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.mapping-table {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
.mapping-toolbar {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.analysis-table {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
.cell-title {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #1f2d3d;
|
|
||||||
}
|
|
||||||
.cell-sub {
|
|
||||||
color: #7c8792;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.inline-filter {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
> *:not(.filter-actions) {
|
|
||||||
flex: 1 1 160px;
|
|
||||||
}
|
|
||||||
.filter-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.report-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
.report-card {
|
|
||||||
border: 1px solid #d6dce1;
|
|
||||||
padding: 12px;
|
|
||||||
background: #fdfdfd;
|
|
||||||
h4 {
|
|
||||||
margin: 0 0 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #2a313c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.plain-tabs ::v-deep .el-tabs__header {
|
|
||||||
border-bottom: 1px solid #d6dce1;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.plain-tabs ::v-deep .el-tabs__content {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -185,28 +185,8 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- <el-table-column label="关联信息" align="center" :show-overflow-tooltip="true">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span v-if="scope.row.parentCoilNos && scope.row.hasMergeSplit === 1 && scope.row.dataType === 1">
|
|
||||||
<el-tag type="warning" size="mini">来自母卷:{{ scope.row.parentCoilNos }}</el-tag>
|
|
||||||
</span>
|
|
||||||
<span v-else-if="scope.row.parentCoilNos && scope.row.dataType === 0">
|
|
||||||
<el-tag type="info" size="mini">分为子卷:{{ scope.row.parentCoilNos }}</el-tag>
|
|
||||||
</span>
|
|
||||||
<span v-else-if="scope.row.parentCoilNos && scope.row.hasMergeSplit === 2">
|
|
||||||
<el-tag type="success" size="mini">合并自:{{ scope.row.parentCoilNos }}</el-tag>
|
|
||||||
</span>
|
|
||||||
<span v-else>—</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column> -->
|
|
||||||
|
|
||||||
<el-table-column v-if="showGrade" label="质量状态" align="center" prop="qualityStatus">
|
<el-table-column v-if="showGrade" label="质量状态" align="center" prop="qualityStatus">
|
||||||
<!-- <template slot-scope="scope">
|
|
||||||
<el-select v-model="scope.row.qualityStatus" placeholder="请选择质量状态" @change="handleGradeChange(scope.row)">
|
|
||||||
<el-option v-for="item in dict.type.coil_quality_status" :key="item.value" :value="item.value"
|
|
||||||
:label="item.label" />
|
|
||||||
</el-select>
|
|
||||||
</template> -->
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="逻辑库位" align="center" prop="warehouseId" v-if="editWarehouse">
|
<el-table-column label="逻辑库位" align="center" prop="warehouseId" v-if="editWarehouse">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
@@ -215,6 +195,8 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<el-table-column v-if="hasTransferType" label="实际库区" align="center" prop="actualWarehouseName" />
|
<el-table-column v-if="hasTransferType" label="实际库区" align="center" prop="actualWarehouseName" />
|
||||||
|
|
||||||
<el-table-column v-if="moreColumn" label="规格" prop="specification"></el-table-column>
|
<el-table-column v-if="moreColumn" label="规格" prop="specification"></el-table-column>
|
||||||
@@ -229,6 +211,7 @@
|
|||||||
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
|
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
|
||||||
|
|
||||||
<el-table-column v-if="hasTransferType" label="调拨类型" align="center" prop="transferType" />
|
<el-table-column v-if="hasTransferType" label="调拨类型" align="center" prop="transferType" />
|
||||||
|
<el-table-column v-if="hasTransferType" label="改判原因" align="center" prop="rejudgeInfo.rejudgeReason" />
|
||||||
|
|
||||||
<el-table-column label="钢卷去向" align="center" prop="nextWarehouseId" v-if="editNext" width="150">
|
<el-table-column label="钢卷去向" align="center" prop="nextWarehouseId" v-if="editNext" width="150">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
@@ -639,7 +622,8 @@ import {
|
|||||||
cancelExportCoil,
|
cancelExportCoil,
|
||||||
checkCoilNo,
|
checkCoilNo,
|
||||||
returnCoil,
|
returnCoil,
|
||||||
getCoilStatisticsList
|
getCoilStatisticsList,
|
||||||
|
listWithAdjustRecordCoil
|
||||||
} from "@/api/wms/coil";
|
} from "@/api/wms/coil";
|
||||||
import { listBoundCoil } from "@/api/wms/deliveryWaybillDetail";
|
import { listBoundCoil } from "@/api/wms/deliveryWaybillDetail";
|
||||||
import { addPendingAction } from "@/api/wms/pendingAction";
|
import { addPendingAction } from "@/api/wms/pendingAction";
|
||||||
@@ -1339,6 +1323,19 @@ export default {
|
|||||||
})
|
})
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.hasTransferType) {
|
||||||
|
listWithAdjustRecordCoil(query).then(response => {
|
||||||
|
this.materialCoilList = response.rows
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
getCoilStatisticsList(statisticQuery).then(res => {
|
||||||
|
this.statistics = res.data || [];
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
listMaterialCoil(query).then(response => {
|
listMaterialCoil(query).then(response => {
|
||||||
this.materialCoilList = response.rows
|
this.materialCoilList = response.rows
|
||||||
this.total = response.total;
|
this.total = response.total;
|
||||||
@@ -1744,6 +1741,7 @@ export default {
|
|||||||
},
|
},
|
||||||
handleExportAll() {
|
handleExportAll() {
|
||||||
const { orderBy, ...query } = this.queryParams;
|
const { orderBy, ...query } = this.queryParams;
|
||||||
|
query.selectType = query.itemType;
|
||||||
this.download('wms/materialCoil/export', query, `materialCoil_${new Date().getTime()}.xlsx`)
|
this.download('wms/materialCoil/export', query, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||||
},
|
},
|
||||||
handleExportAllProps() {
|
handleExportAllProps() {
|
||||||
|
|||||||
Reference in New Issue
Block a user