|
|
<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-form v-if="showProductInfo" :inline="true" label-position="top" class="pi-product-form"> <el-form-item label="产品编码"> <el-input :value="displayText(productInfo.partNo || searchData.partNo)" disabled style="width: 160px" /> </el-form-item> <el-form-item label="产品名称"> <el-input :value="displayText(productInfo.partDesc)" disabled style="width: 220px" /> </el-form-item> <el-form-item label="Item"> <el-input :value="displayText(productInfo.item)" disabled style="width: 120px" /> </el-form-item> <el-form-item label="SKU"> <el-input :value="displayText(productInfo.sku)" disabled style="width: 120px" /> </el-form-item> <el-form-item label="物料分类"> <el-input :value="displayText(productInfo.categoryName)" disabled style="width: 160px" /> </el-form-item> <el-form-item label="物料二级分类"> <el-input :value="displayText(productInfo.categoryLevel2Name)" disabled style="width: 160px" /> </el-form-item> </el-form>
<el-table :data="dataList" :height="tableHeight" border stripe v-loading="loading" style="width: 100%" empty-text="请填写查询条件后点击查询" > <el-table-column fixed="left" label="操作" width="90" header-align="center" align="center"> <template slot-scope="scope"> <el-link style="cursor: pointer" @click="openPriceSheet(scope.row)">价格表</el-link> </template> </el-table-column> <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="priceDate" label="价格日期" min-width="160" header-align="center" align="center"> <template slot-scope="scope"> {{ formatDateOnly(scope.row.priceDate) }} </template> </el-table-column> <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>
<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>
<el-dialog title="价格表" :visible.sync="priceSheetDialogVisible" width="600px" :close-on-click-modal="false" v-drag append-to-body > <el-form :inline="true" label-position="top" class="pi-price-sheet-form"> <el-form-item label="产品"> <el-input :value="displayText(priceSheetHeader.productText)" disabled style="width: 190px" /> </el-form-item> <el-form-item label="价格日期"> <el-input :value="displayText(formatDateOnly(priceSheetHeader.priceDate))" disabled style="width: 80px" /> </el-form-item><!-- <el-form-item label="供应商编码">--><!-- <el-input :value="displayText(priceSheetHeader.supplierId)" disabled style="width: 100px" />--><!-- </el-form-item>--> <el-form-item label="供应商名称"> <el-input :value="displayText(priceSheetHeader.supplierName)" disabled style="width: 180px" /> </el-form-item> <el-form-item label="币种"> <el-input :value="displayText(priceSheetHeader.currency)" disabled style="width: 70px" /> </el-form-item> </el-form>
<el-table :data="priceSheetDetailList" :height="300" border stripe v-loading="priceSheetLoading" style="width: 100%" > <el-table-column prop="qtyStart" label="开始数量" min-width="80" header-align="center" align="right"> <template slot-scope="scope"> {{ formatNumber(scope.row.qtyStart) }} </template> </el-table-column> <el-table-column prop="qtyEnd" label="结束数量" min-width="80" header-align="center" align="right"> <template slot-scope="scope"> {{ formatNumber(scope.row.qtyEnd) }} </template> </el-table-column> <el-table-column prop="unitPrice" label="单价" min-width="80" header-align="center" align="right"> <template slot-scope="scope"> {{ formatNumber(scope.row.unitPrice) }} </template> </el-table-column> <el-table-column prop="remark" label="备注" min-width="150" header-align="center" align="left" show-overflow-tooltip /> </el-table> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="priceSheetDialogVisible = false">关闭</el-button> </div> </el-dialog>
<Chooselist ref="baseList" @getBaseData="getBaseData"> </Chooselist> </div></template>
<script>import echarts from 'echarts'import { searchPartPriceAnalysis } from '@/api/supplier/partPriceAnalysis'import { listTierPriceHistoryDetail } from '@/api/supplier/supplierPartMoqPrice'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())}`}
function createEmptyProductInfo () { return { partNo: '', partDesc: '', item: '', sku: '', categoryName: '', categoryLevel2Name: '' }}
export default { name: 'PartPriceAnalysis', components: { Chooselist }, data () { return { loading: false, tagNo: '', dataList: [], searchData: { site: this.$store.state.user.site, startDate: getDefaultStartDate(), endDate: '', partNo: '', supplierId: '' }, showProductInfo: false, productInfo: createEmptyProductInfo(), priceSheetDialogVisible: false, priceSheetLoading: false, priceSheetHeader: { productText: '', priceDate: '', supplierId: '', supplierName: '', currency: '', historyMainId: null }, priceSheetDetailList: [], 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.showProductInfo = false this.productInfo = createEmptyProductInfo() 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.showProductInfo = true this.productInfo = Object.assign(createEmptyProductInfo(), data.productInfo || {}, { partNo: (data.productInfo && data.productInfo.partNo) || params.partNo }) 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('请求失败') }) }, openPriceSheet (row) { if (!row || !row.historyMainId) { this.$message.warning('当前记录未找到关联价格表') return } const productText = [row.partNo, row.partDesc].filter(Boolean).join(' / ') this.priceSheetHeader = { productText, priceDate: row.priceDate || '', supplierId: row.supplierId || '', supplierName: row.supplierName || '', currency: row.currency || '', historyMainId: row.historyMainId } this.priceSheetDetailList = [] this.priceSheetDialogVisible = true this.priceSheetLoading = true const params = { site: this.$store.state.user.site, historyMainId: row.historyMainId } listTierPriceHistoryDetail(params).then(({ data }) => { if (data && data.code === 0) { this.priceSheetDetailList = data.rows || [] } else { this.priceSheetDetailList = [] this.$message.error((data && data.msg) || '获取价格表明细失败') } this.priceSheetLoading = false }).catch(() => { this.priceSheetLoading = false this.priceSheetDetailList = [] this.$message.error('获取价格表明细失败') }) }, renderChart (list) { if (!this.chartInstance) { this.initChart() } const chartData = [...list].sort((a, b) => { return new Date(a.priceDate).getTime() - new Date(b.priceDate).getTime() }) const xAxisData = chartData.map(item => this.formatChartDate(item.priceDate)) 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.formatDateOnly(row.priceDate)}`, `价格:${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) }, displayText (value) { return value === null || value === undefined || value === '' ? '-' : value }, formatNumber (value) { if (value === null || value === undefined || value === '') { return '-' } const num = Number(value) if (Number.isNaN(num)) { return value } return String(Object.is(num, -0) ? 0 : num) }, 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())}` }, formatDateOnly (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())}` } }}</script>
<style scoped>.part-price-analysis .chart-card { margin-top: 12px;}
.part-price-analysis .pi-product-form { margin-top: 2px; margin-bottom: 10px;}
.part-price-analysis .pi-price-sheet-form { margin-top: 2px;}
.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>
|