3 changed files with 1607 additions and 607 deletions
-
3src/api/supplier/partPriceAnalysis.js
-
1820src/views/modules/srmBaseInformation/srmPartFamily.vue
-
391src/views/modules/supplier/partPriceAnalysis.vue
@ -0,0 +1,3 @@ |
|||||
|
import { createAPI } from '@/utils/httpRequest.js' |
||||
|
|
||||
|
export const searchPartPriceAnalysis = data => createAPI('/supplier/partPriceAnalysis/list', 'post', data) |
||||
1820
src/views/modules/srmBaseInformation/srmPartFamily.vue
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,391 @@ |
|||||
|
<template> |
||||
|
<div class="customer-css part-price-analysis"> |
||||
|
<el-form :inline="true" label-position="top" class="pi-search-form"> |
||||
|
<el-form-item label="开始时间" required> |
||||
|
<el-date-picker |
||||
|
v-model="searchData.startDate" |
||||
|
type="date" |
||||
|
format="yyyy-MM-dd" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
placeholder="开始时间" |
||||
|
style="width: 150px" |
||||
|
clearable |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="结束时间"> |
||||
|
<el-date-picker |
||||
|
v-model="searchData.endDate" |
||||
|
type="date" |
||||
|
format="yyyy-MM-dd" |
||||
|
value-format="yyyy-MM-dd" |
||||
|
placeholder="结束时间" |
||||
|
style="width: 150px" |
||||
|
clearable |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item required> |
||||
|
<span style="cursor: pointer" slot="label" @click="getBaseList(2017)"><a href="javascript:void(0)">物料编码</a></span> |
||||
|
<el-input |
||||
|
v-model="searchData.partNo" |
||||
|
placeholder="物料编码" |
||||
|
style="width: 160px" |
||||
|
clearable |
||||
|
@keyup.enter.native="handleSearch" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item> |
||||
|
<span style="cursor: pointer" slot="label" @click="getBaseList(520)"><a href="javascript:void(0)">供应商编码</a></span> |
||||
|
<el-input |
||||
|
v-model="searchData.supplierId" |
||||
|
placeholder="不填则按物料查询" |
||||
|
style="width: 160px" |
||||
|
clearable |
||||
|
@keyup.enter.native="handleSearch" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item label=" "> |
||||
|
<el-button type="primary" class="customer-bun-min" :loading="loading" @click="handleSearch">查询</el-button> |
||||
|
<el-button class="customer-bun-min" @click="resetSearch">重置</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
|
||||
|
<el-table |
||||
|
:data="dataList" |
||||
|
:height="tableHeight" |
||||
|
border |
||||
|
stripe |
||||
|
v-loading="loading" |
||||
|
style="width: 100%" |
||||
|
empty-text="请填写查询条件后点击查询" |
||||
|
> |
||||
|
<el-table-column prop="partNo" label="物料编码" min-width="80" header-align="center" align="left" show-overflow-tooltip /> |
||||
|
<el-table-column prop="partDesc" label="物料名称" min-width="250" header-align="center" align="left" show-overflow-tooltip /> |
||||
|
<el-table-column prop="price" label="报价" min-width="80" header-align="center" align="right"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ formatMoney(scope.row.price) }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="priceChangeRate" label="涨跌" min-width="100" header-align="center" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span :class="getPriceChangeClass(scope.row.priceChangeRate)"> |
||||
|
{{ scope.row.priceChangeRate || '-' }} |
||||
|
</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="createdDate" label="报价时间" min-width="160" header-align="center" align="center" /> |
||||
|
<el-table-column prop="supplierId" label="报价供应商编码" min-width="120" header-align="center" align="left" show-overflow-tooltip /> |
||||
|
<el-table-column prop="supplierName" label="报价供应商名称" min-width="200" header-align="center" align="left" show-overflow-tooltip /> |
||||
|
<el-table-column prop="categoryName" label="物料分类" min-width="100" header-align="center" align="left" show-overflow-tooltip /> |
||||
|
<el-table-column prop="categoryLevel2Name" label="物料二级分类" min-width="100" header-align="center" align="left" show-overflow-tooltip /> |
||||
|
<el-table-column prop="categoryLevel3Name" label="物料三级分类" min-width="100" header-align="center" align="left" show-overflow-tooltip /> |
||||
|
</el-table> |
||||
|
|
||||
|
<el-card shadow="never" class="chart-card"> |
||||
|
<div slot="header" class="chart-card__header"> |
||||
|
<span>价格趋势(已接受报价)</span> |
||||
|
</div> |
||||
|
<div id="partPriceChart" class="chart-box" :style="{ height: chartHeight + 'px' }"></div> |
||||
|
</el-card> |
||||
|
|
||||
|
<Chooselist |
||||
|
ref="baseList" |
||||
|
@getBaseData="getBaseData"> |
||||
|
</Chooselist> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import echarts from 'echarts' |
||||
|
import { searchPartPriceAnalysis } from '@/api/supplier/partPriceAnalysis' |
||||
|
import Chooselist from '@/views/modules/common/Chooselist_eam' |
||||
|
|
||||
|
function getDefaultStartDate () { |
||||
|
const date = new Date() |
||||
|
date.setMonth(date.getMonth() - 3) |
||||
|
const pad = num => String(num).padStart(2, '0') |
||||
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` |
||||
|
} |
||||
|
|
||||
|
export default { |
||||
|
name: 'PartPriceAnalysis', |
||||
|
components: { |
||||
|
Chooselist |
||||
|
}, |
||||
|
data () { |
||||
|
return { |
||||
|
loading: false, |
||||
|
tagNo: '', |
||||
|
dataList: [], |
||||
|
searchData: { |
||||
|
site: this.$store.state.user.site, |
||||
|
startDate: getDefaultStartDate(), |
||||
|
endDate: '', |
||||
|
partNo: '', |
||||
|
supplierId: '' |
||||
|
}, |
||||
|
tableHeight: 280, |
||||
|
chartHeight: 320, |
||||
|
chartInstance: null |
||||
|
} |
||||
|
}, |
||||
|
mounted () { |
||||
|
this.$nextTick(() => { |
||||
|
this.updateLayout() |
||||
|
this.initChart() |
||||
|
}) |
||||
|
window.addEventListener('resize', this.handleResize) |
||||
|
}, |
||||
|
activated () { |
||||
|
if (this.chartInstance) { |
||||
|
this.chartInstance.resize() |
||||
|
} |
||||
|
}, |
||||
|
beforeDestroy () { |
||||
|
window.removeEventListener('resize', this.handleResize) |
||||
|
if (this.chartInstance) { |
||||
|
this.chartInstance.dispose() |
||||
|
this.chartInstance = null |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
updateLayout () { |
||||
|
const availableHeight = window.innerHeight - 220 |
||||
|
this.tableHeight = Math.max(220, Math.floor(availableHeight * 0.45)) |
||||
|
this.chartHeight = Math.max(260, Math.floor(availableHeight * 0.5)) |
||||
|
}, |
||||
|
handleResize () { |
||||
|
this.updateLayout() |
||||
|
if (this.chartInstance) { |
||||
|
this.chartInstance.resize() |
||||
|
} |
||||
|
}, |
||||
|
initChart () { |
||||
|
const dom = document.getElementById('partPriceChart') |
||||
|
if (!dom) { |
||||
|
return |
||||
|
} |
||||
|
if (this.chartInstance) { |
||||
|
this.chartInstance.dispose() |
||||
|
} |
||||
|
this.chartInstance = echarts.init(dom) |
||||
|
this.renderChart([]) |
||||
|
}, |
||||
|
resetSearch () { |
||||
|
this.searchData = { |
||||
|
site: this.$store.state.user.site, |
||||
|
startDate: getDefaultStartDate(), |
||||
|
endDate: '', |
||||
|
partNo: '', |
||||
|
supplierId: '' |
||||
|
} |
||||
|
this.dataList = [] |
||||
|
this.renderChart([]) |
||||
|
}, |
||||
|
getBaseList (val) { |
||||
|
this.tagNo = val |
||||
|
this.$nextTick(() => { |
||||
|
let strVal = '' |
||||
|
let conSql = '' |
||||
|
if (val === 520) { |
||||
|
strVal = this.searchData.supplierId || '' |
||||
|
conSql = " and supplier_no <> '' " |
||||
|
} else if (val === 2017) { |
||||
|
strVal = this.searchData.partNo || '' |
||||
|
conSql = " and part_no <> '' " |
||||
|
} |
||||
|
this.$refs.baseList.init(val, strVal, conSql) |
||||
|
}) |
||||
|
}, |
||||
|
getBaseData (val) { |
||||
|
if (this.tagNo === 520) { |
||||
|
this.searchData.supplierId = val.supplier_no || val.code || '' |
||||
|
} else if (this.tagNo === 2017) { |
||||
|
this.searchData.partNo = val.part_no || val.code || '' |
||||
|
} |
||||
|
}, |
||||
|
validateSearch () { |
||||
|
if (!this.searchData.startDate) { |
||||
|
this.$message.warning('请选择开始时间') |
||||
|
return false |
||||
|
} |
||||
|
if (!this.searchData.partNo || !this.searchData.partNo.trim()) { |
||||
|
this.$message.warning('请输入物料编码') |
||||
|
return false |
||||
|
} |
||||
|
if (this.searchData.endDate && this.searchData.startDate > this.searchData.endDate) { |
||||
|
this.$message.warning('开始时间不能大于结束时间') |
||||
|
return false |
||||
|
} |
||||
|
return true |
||||
|
}, |
||||
|
handleSearch () { |
||||
|
if (!this.validateSearch()) { |
||||
|
return |
||||
|
} |
||||
|
this.loading = true |
||||
|
const params = { |
||||
|
site: this.$store.state.user.site, |
||||
|
startDate: this.searchData.startDate, |
||||
|
endDate: this.searchData.endDate || '', |
||||
|
partNo: this.searchData.partNo.trim(), |
||||
|
supplierId: this.searchData.supplierId ? this.searchData.supplierId.trim() : '' |
||||
|
} |
||||
|
searchPartPriceAnalysis(params).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.dataList = data.list || [] |
||||
|
this.renderChart(this.dataList) |
||||
|
if (this.dataList.length === 0) { |
||||
|
this.$message.info('未查询到已接受报价记录') |
||||
|
} |
||||
|
} else { |
||||
|
this.$message.error((data && data.msg) || '查询失败') |
||||
|
} |
||||
|
this.loading = false |
||||
|
}).catch(() => { |
||||
|
this.loading = false |
||||
|
this.$message.error('请求失败') |
||||
|
}) |
||||
|
}, |
||||
|
renderChart (list) { |
||||
|
if (!this.chartInstance) { |
||||
|
this.initChart() |
||||
|
} |
||||
|
const chartData = [...list].sort((a, b) => { |
||||
|
return new Date(a.createdDate).getTime() - new Date(b.createdDate).getTime() |
||||
|
}) |
||||
|
const xAxisData = chartData.map(item => this.formatChartDate(item.createdDate)) |
||||
|
const seriesData = chartData.map(item => item.price) |
||||
|
const titleSuffix = this.searchData.supplierId |
||||
|
? `${this.searchData.partNo} / ${this.searchData.supplierId}` |
||||
|
: this.searchData.partNo |
||||
|
|
||||
|
const option = { |
||||
|
title: { |
||||
|
text: chartData.length ? `物料价格趋势 - ${titleSuffix}` : '暂无数据', |
||||
|
left: 'center', |
||||
|
textStyle: { |
||||
|
fontSize: 14, |
||||
|
fontWeight: 'normal' |
||||
|
} |
||||
|
}, |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
formatter: (params) => { |
||||
|
if (!params || !params.length) { |
||||
|
return '' |
||||
|
} |
||||
|
const index = params[0].dataIndex |
||||
|
const row = chartData[index] || {} |
||||
|
return [ |
||||
|
`报价时间:${this.formatDateTime(row.createdDate)}`, |
||||
|
`报价:${this.formatMoney(row.price)}`, |
||||
|
`供应商:${row.supplierId || '-'} ${row.supplierName || ''}` |
||||
|
].join('<br/>') |
||||
|
} |
||||
|
}, |
||||
|
grid: { |
||||
|
left: '3%', |
||||
|
right: '4%', |
||||
|
bottom: '12%', |
||||
|
containLabel: true |
||||
|
}, |
||||
|
xAxis: { |
||||
|
type: 'category', |
||||
|
boundaryGap: false, |
||||
|
data: xAxisData, |
||||
|
axisLabel: { |
||||
|
rotate: xAxisData.length > 8 ? 35 : 0 |
||||
|
} |
||||
|
}, |
||||
|
yAxis: { |
||||
|
type: 'value', |
||||
|
name: '价格', |
||||
|
scale: true |
||||
|
}, |
||||
|
series: [{ |
||||
|
name: '报价', |
||||
|
type: 'line', |
||||
|
smooth: true, |
||||
|
symbol: 'circle', |
||||
|
symbolSize: 6, |
||||
|
data: seriesData, |
||||
|
connectNulls: true |
||||
|
}] |
||||
|
} |
||||
|
this.chartInstance.setOption(option, true) |
||||
|
this.$nextTick(() => { |
||||
|
if (this.chartInstance) { |
||||
|
this.chartInstance.resize() |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
formatMoney (value) { |
||||
|
if (value === null || value === undefined || value === '') { |
||||
|
return '-' |
||||
|
} |
||||
|
return new Intl.NumberFormat('zh-CN', { |
||||
|
minimumFractionDigits: 2, |
||||
|
maximumFractionDigits: 2 |
||||
|
}).format(value) |
||||
|
}, |
||||
|
getPriceChangeClass (value) { |
||||
|
if (!value || value === '-') { |
||||
|
return '' |
||||
|
} |
||||
|
if (value.startsWith('+')) { |
||||
|
return 'price-up' |
||||
|
} |
||||
|
if (value.startsWith('-')) { |
||||
|
return 'price-down' |
||||
|
} |
||||
|
return '' |
||||
|
}, |
||||
|
formatChartDate (value) { |
||||
|
if (!value) { |
||||
|
return '' |
||||
|
} |
||||
|
const date = new Date(value) |
||||
|
if (Number.isNaN(date.getTime())) { |
||||
|
return value |
||||
|
} |
||||
|
const pad = num => String(num).padStart(2, '0') |
||||
|
const year = String(date.getFullYear()).slice(-2) |
||||
|
return `${year}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` |
||||
|
}, |
||||
|
formatDateTime (value) { |
||||
|
if (!value) { |
||||
|
return '' |
||||
|
} |
||||
|
const date = new Date(value) |
||||
|
if (Number.isNaN(date.getTime())) { |
||||
|
return value |
||||
|
} |
||||
|
const pad = num => String(num).padStart(2, '0') |
||||
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}` |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.part-price-analysis .chart-card { |
||||
|
margin-top: 12px; |
||||
|
} |
||||
|
|
||||
|
.part-price-analysis .chart-card__header { |
||||
|
font-size: 14px; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.part-price-analysis .chart-box { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.part-price-analysis .price-up { |
||||
|
color: #f56c6c; |
||||
|
} |
||||
|
|
||||
|
.part-price-analysis .price-down { |
||||
|
color: #67c23a; |
||||
|
} |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue