常熟吴彦祖 1 week ago
parent
commit
ef5d054c70
  1. 3
      src/api/supplier/partPriceAnalysis.js
  2. 1820
      src/views/modules/srmBaseInformation/srmPartFamily.vue
  3. 391
      src/views/modules/supplier/partPriceAnalysis.vue

3
src/api/supplier/partPriceAnalysis.js

@ -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

391
src/views/modules/supplier/partPriceAnalysis.vue

@ -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>
Loading…
Cancel
Save