Compare commits
3 Commits
dfa4ff3f54
...
2d200dd1a6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d200dd1a6 | ||
|
|
a907c7273e | ||
|
|
694beb5851 |
7
.env.dev
7
.env.dev
@@ -5,7 +5,10 @@ ENV = 'development'
|
||||
VUE_APP_TITLE = 洛玻集团驾驶舱
|
||||
|
||||
# 芋道管理系统/开发环境
|
||||
VUE_APP_BASE_API = 'http://172.16.32.18:7070'
|
||||
# VUE_APP_BASE_API = 'http://172.16.32.18:7070'
|
||||
# VUE_APP_BASE_API = 'http://172.16.32.95:7070'
|
||||
VUE_APP_BASE_API = 'http://172.16.33.83:7070'
|
||||
|
||||
# VUE_APP_BASE_API = 'http://192.168.0.35:7070'
|
||||
|
||||
|
||||
@@ -22,4 +25,4 @@ VUE_APP_CAPTCHA_ENABLE = true
|
||||
VUE_APP_DOC_ENABLE = true
|
||||
|
||||
# 百度统计
|
||||
VUE_APP_BAIDU_CODE = fadc1bd5db1a1d6f581df60a1807f8ab
|
||||
# VUE_APP_BAIDU_CODE = fadc1bd5db1a1d6f581df60a1807f8ab
|
||||
|
||||
12
.env.prod
12
.env.prod
@@ -1,16 +1,20 @@
|
||||
# 生产环境配置
|
||||
NODE_ENV = 'production'
|
||||
ENV = 'production'
|
||||
|
||||
# 页面标题
|
||||
VUE_APP_TITLE = 洛玻集团驾驶舱
|
||||
|
||||
# 芋道管理系统/生产环境
|
||||
VUE_APP_BASE_API = 'http://192.168.0.35:7070'
|
||||
# VUE_APP_BASE_API = '/prod-api'
|
||||
VUE_APP_BASE_API = ''
|
||||
|
||||
# 根据服务器或域名修改
|
||||
# PUBLIC_PATH = 'http://192.168.0.35:7070'
|
||||
# PUBLIC_PATH = 'http://my-pi.com:8888/yudao-admin/'
|
||||
# PUBLIC_PATH = 'http://192.168.0.33:8888/'
|
||||
PUBLIC_PATH = ''
|
||||
|
||||
# 二级部署路径
|
||||
# VUE_APP_APP_NAME ='yudao-admin'
|
||||
VUE_APP_APP_NAME ='yudao-admin'
|
||||
|
||||
# 多租户的开关
|
||||
VUE_APP_TENANT_ENABLE = true
|
||||
|
||||
@@ -43,3 +43,36 @@ export function getAccountSumaryPage(data) {
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function getProfitImpactList(data) {
|
||||
return request({
|
||||
url: "/lb/cost-analysis/profitImpactList",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function getSalesRevenueData(data) {
|
||||
return request({
|
||||
url: "lb/sales-revenue/getData",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function getOperateCockpit(data) {
|
||||
return request({
|
||||
url: "/lb/operate-cockpit/get",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function getOrderDetail(data) {
|
||||
return request({
|
||||
url: "/lb/operate-cockpit/getOrderDetail",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
1
src/assets/icons/svg/orderReturn.svg
Normal file
1
src/assets/icons/svg/orderReturn.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1763973887412" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5048" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M538.622846 950.291255c257.534448-171.658591 308.738643-462.119963-75.504648-453.027887l0 225.514173L121.893676 381.529483 463.118198 40.281425l0 220.701564c218.259956-5.67321 470.02705 96.270601 470.02705 280.69596C933.146271 813.119105 768.337691 874.762047 538.622846 950.291255" fill="#1296db" p-id="5049"></path></svg>
|
||||
|
After Width: | Height: | Size: 656 B |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 680 KiB |
@@ -143,12 +143,6 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
const startTime = moment().startOf("week").format("YYYY-MM-DD");
|
||||
const endTime = moment().format("YYYY-MM-DD");
|
||||
console.log(this.date, "date");
|
||||
this.date = [startTime, endTime];
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -161,9 +155,9 @@ export default {
|
||||
methods: {
|
||||
getData(obj) {
|
||||
getProductSaleAnalysis({
|
||||
startTime: 1762704000000,
|
||||
endTime: 1762790400000,
|
||||
mode: 1
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}).then((res) => {
|
||||
console.log(res);
|
||||
this.productSaleData = [
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lineBottom" style="height: 214px; width: 100%">
|
||||
<!-- 传递 series 数据给子组件,添加 key 确保数据更新时重新渲染 -->
|
||||
<coreLineChart style="height: 214px; width: 680px" :chart-series="chartSeries"
|
||||
:key="JSON.stringify(chartSeries)" />
|
||||
<div class="lineBottom" style="height: 213px; width: 100%">
|
||||
<!-- 传递动态生成的 series 数据和 xAxis 数据给子组件 -->
|
||||
<coreLineChart style="height: 213px; width: 680px" :chart-series="chartSeries" :x-axis-data="xAxisData"
|
||||
:dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -25,80 +25,97 @@ import * as echarts from 'echarts';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: { coreLineChart },
|
||||
props: ["name", "size", "icon"],
|
||||
props: {
|
||||
line: { // 接收父组件传递的 cost 数据对象
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeButton: undefined,
|
||||
itemList: [
|
||||
{ unit: "单价·元/m²", targetValue: 16, currentValue: 14.5, progress: 90 },
|
||||
{ unit: "净价·元/m²", targetValue: 16, currentValue: 15.2, progress: 85 },
|
||||
{ unit: "销量·万m²", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "双镀面板·万m²", targetValue: 15, currentValue: 13.8, progress: 92 },
|
||||
],
|
||||
// 定义要传递的 series 数据(对应图例的三个费用类型)
|
||||
chartSeries: [
|
||||
{
|
||||
// 图表样式配置项,可以抽离出来方便管理
|
||||
chartConfig: {
|
||||
manageCost: {
|
||||
name: '管理费用',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(11, 88, 255, .5)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(11, 88, 255, .5)',
|
||||
borderColor: 'rgba(11, 88, 255, 1)',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.5,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(11, 88, 255, .4)' },
|
||||
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [140, 232, 101, 264, 90, 340] // 与 xAxis 数据长度一致(6个月份)
|
||||
lineColor: 'rgba(11, 88, 255, .5)',
|
||||
itemColor: 'rgba(11, 88, 255, .5)',
|
||||
borderColor: 'rgba(11, 88, 255, 1)',
|
||||
areaGradient: [
|
||||
{ offset: 0, color: 'rgba(11, 88, 255, .4)' },
|
||||
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
||||
]
|
||||
},
|
||||
{
|
||||
saleCost: {
|
||||
name: '销售费用',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(54, 181, 138, .5)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(54, 181, 138, .5)',
|
||||
borderColor: 'rgba(54, 181, 138, 1)',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.5,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(54, 181, 138, .4)' },
|
||||
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [120, 282, 111, 234, 220, 340]
|
||||
lineColor: 'rgba(54, 181, 138, .5)',
|
||||
itemColor: 'rgba(54, 181, 138, .5)',
|
||||
borderColor: 'rgba(54, 181, 138, 1)',
|
||||
areaGradient: [
|
||||
{ offset: 0, color: 'rgba(54, 181, 138, .4)' },
|
||||
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
||||
]
|
||||
},
|
||||
{
|
||||
financeCost: {
|
||||
name: '财务费用',
|
||||
lineColor: 'rgba(255, 132, 0, .5)',
|
||||
itemColor: 'rgba(255, 132, 0, .5)',
|
||||
borderColor: 'rgba(255, 132, 0, 1)',
|
||||
areaGradient: [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, .4)' },
|
||||
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 动态生成 X 轴数据
|
||||
xAxisData() {
|
||||
// 从 cost.line 中获取任意一个有数据的键的 keys 作为 X 轴
|
||||
const lineData = this.line || {};
|
||||
const firstKey = Object.keys(lineData)[0];
|
||||
return firstKey ? Object.keys(lineData[firstKey]) : [];
|
||||
},
|
||||
// 动态生成 series 数据
|
||||
chartSeries() {
|
||||
const lineData = this.line || {};
|
||||
const xAxisKeys = this.xAxisData;
|
||||
|
||||
// 如果没有 X 轴数据,则返回空数组
|
||||
if (xAxisKeys.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 遍历配置项,生成 series
|
||||
return Object.keys(this.chartConfig).map(key => {
|
||||
const config = this.chartConfig[key];
|
||||
// 确保数据顺序和 X 轴一致
|
||||
const dataValues = xAxisKeys.map(date => lineData[key] ? lineData[key][date] : 0);
|
||||
|
||||
return {
|
||||
name: config.name,
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(255, 132, 0, .5)' },
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: config.lineColor },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, .5)',
|
||||
borderColor: 'rgba(255, 132, 0, 1)',
|
||||
color: config.itemColor,
|
||||
borderColor: config.borderColor,
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.5,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, .4)' },
|
||||
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
||||
]),
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, config.areaGradient),
|
||||
},
|
||||
data: [90, 150, 180, 120, 250, 280]
|
||||
}
|
||||
]
|
||||
};
|
||||
data: dataValues
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
};
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
export default {
|
||||
name: 'Header',
|
||||
props: {
|
||||
@@ -97,44 +98,40 @@ export default {
|
||||
const mode = this.activeTime + 1; // 1=日,2=月,3=年
|
||||
const defaultMoment = moment(); // 默认当前时间
|
||||
|
||||
// 处理选择的日期:转为moment对象(兼容不同选择器格式)
|
||||
console.log('this.date', this.date);
|
||||
|
||||
const targetMoment = this.date
|
||||
? moment(this.date, this.getPickerType === 'date' ? 'YYYY-MM-DD' : (this.getPickerType === 'month' ? 'YYYY-MM' : 'YYYY'))
|
||||
: defaultMoment;
|
||||
|
||||
// 验证日期有效性
|
||||
if (!targetMoment.isValid()) {
|
||||
console.error('无效日期:', this.date);
|
||||
return { startTime, endTime, mode };
|
||||
}
|
||||
|
||||
// 1. 日维度:当天0点 → 次日0点
|
||||
// 1. 日维度:00:00:00 → 23:59:59(无毫秒)
|
||||
if (this.activeTime === 0) {
|
||||
startTime = targetMoment.startOf('day').valueOf(); // 当天00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'day').startOf('day').valueOf(); // 次日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('day').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('day').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 2. 月维度:当月1日0点 → 次月1日0点
|
||||
// 2. 月维度:当月1日00:00:00 → 当月最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 1) {
|
||||
startTime = targetMoment.startOf('month').valueOf(); // 当月1日00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'month').startOf('month').valueOf(); // 次月1日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('month').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('month').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 3. 年维度:当年1月1日0点 → 次年1月1日0点
|
||||
// 3. 年维度:当年1月1日00:00:00 → 当年最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 2) {
|
||||
startTime = targetMoment.startOf('year').valueOf(); // 当年1月1日00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'year').startOf('year').valueOf(); // 次年1月1日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('year').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('year').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 调试输出(格式化显示,便于验证)
|
||||
// 调试输出:验证是否去掉毫秒
|
||||
console.log('时间范围计算结果:', {
|
||||
mode,
|
||||
startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
startTimeStamp: startTime,
|
||||
endTimeStamp: endTime
|
||||
startTime: moment(startTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 00:00:00
|
||||
endTime: moment(endTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 23:59:59(无毫秒)
|
||||
startTimeStamp: startTime, // 秒级时间戳(如:1764422400)
|
||||
endTimeStamp: endTime // 秒级时间戳(如:1764508799)
|
||||
});
|
||||
|
||||
return { startTime, endTime, mode };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div style="">
|
||||
<Container name="利润主要影响因素" icon="cockpitItemIcon" size="profitMiddleBasic" topSize="KFAPTopTitle">
|
||||
<Container name="利润主要影响因素·万元" icon="cockpitItemIcon" size="profitMiddleBasic" topSize="KFAPTopTitle">
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;width: 100%;">
|
||||
<div class="left" style="width: 382px;">
|
||||
<top-item :itemList="targetItemList" />
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
<span class="title-text">
|
||||
{{ name }}
|
||||
</span>
|
||||
<svg-icon @click="handleShow" v-if="tableShow" class="title-icon" style="position: absolute;right: 20;" :icon-class="'orderReturn'" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-body">
|
||||
@@ -21,12 +23,16 @@ export default {
|
||||
name: 'Container',
|
||||
components: {},
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
props: ['name', 'size', 'icon', 'nameTwo'],
|
||||
props: ['name', 'size', 'icon', 'nameTwo','tableShow'],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
methods: {
|
||||
handleShow() {
|
||||
this.$emit('handleShow',false)
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -83,7 +89,7 @@ export default {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
border: 1px solid;
|
||||
border-image: linear-gradient(277deg, rgba(255, 255, 255, 0), rgba(92, 140, 255, 1)) 1 1;
|
||||
|
||||
@@ -42,40 +42,48 @@
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
props: ["name", "size", "icon"],
|
||||
props: ["purchase"],
|
||||
data() {
|
||||
return {
|
||||
progress: 90,
|
||||
itemList: [
|
||||
{
|
||||
unit: "本月增效额·万元",
|
||||
targetValue: 16,
|
||||
currentValue: 14.5,
|
||||
progress: 90
|
||||
},
|
||||
{
|
||||
unit: "累计增效额·万元",
|
||||
targetValue: 16,
|
||||
currentValue: 15.2,
|
||||
progress: 85
|
||||
},
|
||||
// {
|
||||
// unit: "销量·万㎡",
|
||||
// targetValue: 20,
|
||||
// currentValue: 16,
|
||||
// progress: 80
|
||||
// },
|
||||
// {
|
||||
// unit: "双镀面板·万㎡",
|
||||
// targetValue: 15,
|
||||
// currentValue: 13.8,
|
||||
// progress: 92
|
||||
// }
|
||||
]
|
||||
itemList: []
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
purchase: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.itemList = this.transformData(newVal);
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
transformData(rawData) {
|
||||
const dataMap = [
|
||||
{
|
||||
key: 'increase',
|
||||
unit: '本月增效额·万元'
|
||||
},
|
||||
{
|
||||
key: 'totalIncrease',
|
||||
unit: '累计增效额·万元'
|
||||
}
|
||||
];
|
||||
|
||||
return dataMap.map(itemInfo => {
|
||||
const rawItem = rawData[itemInfo.key] || {};
|
||||
const progress = Math.max(0, Math.round((rawItem.rate || 0)));
|
||||
|
||||
return {
|
||||
unit: itemInfo.unit,
|
||||
targetValue: rawItem.target || 0,
|
||||
currentValue: rawItem.real || 0,
|
||||
progress: progress
|
||||
};
|
||||
});
|
||||
},
|
||||
getTargetColor(currentValue, targetValue) {
|
||||
return currentValue >= targetValue
|
||||
? "rgba(98, 213, 180, 1)"
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
</div>
|
||||
<div class="lineBottom" style="height: 219px; width: 100%">
|
||||
<!-- 传递当前选中的 series 数据给子组件,key 确保数据更新时重新渲染 -->
|
||||
<coreLineChart style="height: 219px; width: 500px" :chart-series="currentSeries"
|
||||
:key="activeButton + JSON.stringify(currentSeries)" />
|
||||
<coreLineChart style="height: 219px; width: 500px" :chart-series="currentSeries" :x-axis-data="xAxisData"
|
||||
:dateData="dateData" :key="activeButton + JSON.stringify(currentSeries)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -42,204 +42,102 @@ import * as echarts from 'echarts';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: { coreLineChart },
|
||||
props: ["name", "size", "icon"],
|
||||
props: {
|
||||
line: { // 接收父组件传递的 sale 数据对象
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
dateData: { // 接收父组件传递的 sale 数据对象
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeButton: 0,
|
||||
itemList: [
|
||||
{ unit: "单价·元/m²", targetValue: 16, currentValue: 14.5, progress: 90 },
|
||||
{ unit: "净价·元/m²", targetValue: 16, currentValue: 15.2, progress: 85 },
|
||||
{ unit: "销量·万m²", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "双镀面板·万m²", targetValue: 15, currentValue: 13.8, progress: 92 },
|
||||
],
|
||||
// 4个按钮对应的 series 数据(目标+实际两条线)
|
||||
seriesMap: [
|
||||
// 0: 单价(元/m²)
|
||||
[
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(91, 230, 190, 1)',
|
||||
borderColor: 2
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [16, 16.2, 15.8, 16.1, 15.9, 16] // 6-11月目标数据
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: 'rgba(255, 132, 0, 1)',
|
||||
borderWidth: 2,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [14.5, 14.8, 15.2, 14.6, 15, 14.7] // 6-11月实际数据
|
||||
}
|
||||
],
|
||||
// 1: 净价(元/m²)
|
||||
[
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(91, 230, 190, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [16, 16.1, 15.9, 16.2, 16, 16.1]
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [15.2, 15.5, 15.3, 15.6, 15.4, 15.5]
|
||||
}
|
||||
],
|
||||
// 2: 销量(万m²)
|
||||
[
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(91, 230, 190, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [20, 20.5, 19.8, 21, 20.2, 20.8]
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [16, 16.8, 17.2, 16.5, 17, 17.5]
|
||||
}
|
||||
],
|
||||
// 3: 双镀产品(万m²)
|
||||
[
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(91, 230, 190, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [15, 15.2, 14.8, 15.5, 15.1, 15.3]
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [13.8, 14.2, 14, 14.5, 14.3, 14.6]
|
||||
}
|
||||
]
|
||||
// 定义按钮与 line 数据中 key 的映射关系
|
||||
buttonToDataKey: [
|
||||
'单价',
|
||||
'净价',
|
||||
'销量',
|
||||
'双镀面板' // 注意:数据中的 key 是“双镀面板”,按钮显示的是“双镀产品”
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 动态生成 X 轴数据
|
||||
xAxisData() {
|
||||
const lineData = this.line || {};
|
||||
// 获取当前激活按钮对应的数据
|
||||
const currentDataKey = this.buttonToDataKey[this.activeButton];
|
||||
const currentIndicatorData = lineData[currentDataKey];
|
||||
|
||||
// 使用 'target' 的键作为 x 轴,如果 'target' 不存在,则使用 'real' 的键
|
||||
if (currentIndicatorData && currentIndicatorData.target) {
|
||||
return Object.keys(currentIndicatorData.target);
|
||||
} else if (currentIndicatorData && currentIndicatorData.real) {
|
||||
return Object.keys(currentIndicatorData.real);
|
||||
}
|
||||
return [];
|
||||
},
|
||||
// 根据激活按钮动态返回对应 series 数据
|
||||
currentSeries() {
|
||||
return this.seriesMap[this.activeButton] || [];
|
||||
const lineData = this.line || {};
|
||||
const currentDataKey = this.buttonToDataKey[this.activeButton];
|
||||
const chartData = lineData[currentDataKey];
|
||||
|
||||
if (!chartData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 提取目标和实际数据的值,并确保顺序与 X 轴一致
|
||||
const xAxisKeys = this.xAxisData;
|
||||
const targetDataValues = xAxisKeys.map(date => chartData.target ? chartData.target[date] : 0);
|
||||
const realDataValues = xAxisKeys.map(date => chartData.real ? chartData.real[date] : 0);
|
||||
|
||||
return [
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(91, 230, 190, 1)',
|
||||
borderColor: 2
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: targetDataValues
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)', width: 2 },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: 'rgba(255, 132, 0, 1)',
|
||||
borderWidth: 2,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: realDataValues
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<Container name="采购重点指标" nameTwo="存货重点指标" icon="cockpitItemIcon" size="bottomBasic">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="bottom-left-content" style="display: flex;gap: 9px;padding: 14px 16px;">
|
||||
<coreBottomLeftItem>
|
||||
<coreBottomLeftItem :purchase="purchase">
|
||||
</coreBottomLeftItem>
|
||||
<div class="content-right" style="background: #F9FCFF;padding: 15px 12px;">
|
||||
<base-table style="height: 180px;width: 260px;" :page="1" :limit="10" :show-index="true" :beilv="1"
|
||||
@@ -25,11 +25,11 @@ export default {
|
||||
components: { Container, coreBottomLeftItem, baseTable },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
purchase: { // 接收父组件传递的设备数据数组
|
||||
type: Object,
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
inventory: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
@@ -37,12 +37,12 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
maintenanceTasks: [
|
||||
{ id: 1, eqName: '纯碱', taskName: '1313,252', },
|
||||
{ id: 2, eqName: '硅砂', taskName: '14,252', },
|
||||
{ id: 2, eqName: '白云石', taskName: '23,252', },
|
||||
{ id: 2, eqName: '石灰石', taskName: '34,421', },
|
||||
{ id: 2, eqName: '氧化铝', taskName: '1,251.34', },
|
||||
{ id: 2, eqName: '氢氧化铝', taskName: '14,252', },
|
||||
{ id: 1, name: '纯碱', number: '1313,252', },
|
||||
{ id: 2, name: '硅砂', number: '14,252', },
|
||||
{ id: 2, name: '白云石', number: '23,252', },
|
||||
{ id: 2, name: '石灰石', number: '34,421', },
|
||||
{ id: 2, name: '氧化铝', number: '1,251.34', },
|
||||
{ id: 2, name: '氢氧化铝', number: '14,252', },
|
||||
|
||||
// { id: 2, eqName: '螺杆挤出', taskName: '例行维护', },
|
||||
// { id: 2, eqName: '螺杆挤出', taskName: '例行维护', },
|
||||
@@ -53,22 +53,26 @@ export default {
|
||||
],
|
||||
tableProps: [
|
||||
// { prop: 'id', label: '序号', width: 50, align: 'center' },
|
||||
{ prop: 'eqName', label: '物料', align: 'left' },
|
||||
{ prop: 'taskName', label: '库存/吨', align: 'left' },
|
||||
{ prop: 'name', label: '物料', align: 'left' },
|
||||
{ prop: 'number', label: '库存/吨', align: 'left' },
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
inventory: {
|
||||
handler(newInventoryData) {
|
||||
// 当 inventory 数据变化时,执行转换函数
|
||||
this.maintenanceTasks = this.transformInventoryData(newInventoryData);
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
immediate: true, // 组件初始化时立即执行一次
|
||||
deep: true // 深度监听对象内部属性的变化
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
// 组件挂载时,如果有初始 inventory 数据,也执行一次转换
|
||||
if (this.inventory) {
|
||||
this.maintenanceTasks = this.transformInventoryData(this.inventory);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
@@ -78,115 +82,25 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
/**
|
||||
* 核心转换函数:将 inventory 对象转换为 maintenanceTasks 数组
|
||||
* @param {Object} inventoryData - 格式如: { "纯碱": 0, "硅砂": 0, ... }
|
||||
* @returns {Array} - 格式如: [ { id: 1, name: '纯碱', number: '0' }, ... ]
|
||||
*/
|
||||
transformInventoryData(inventoryData) {
|
||||
// 检查输入是否为有效的非空对象
|
||||
if (!inventoryData || typeof inventoryData !== 'object' || Object.keys(inventoryData).length === 0) {
|
||||
return []; // 如果无效,返回空数组
|
||||
}
|
||||
|
||||
this.chart.setOption(option)
|
||||
// 使用 Object.entries() 和 map() 进行转换
|
||||
return Object.entries(inventoryData).map(([name, value], index) => {
|
||||
return {
|
||||
id: index + 1, // id 从 1 开始自增
|
||||
name: name, // 物料名称
|
||||
number: String(value) // 将数值转换为字符串,以匹配 "number" 字段
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div class="top" style="display: flex; width: 100%;">
|
||||
<top-item />
|
||||
<top-item :sale="sale" :dateData="dateData" />
|
||||
</div>
|
||||
<div class="bottom"
|
||||
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- <top-item /> -->
|
||||
<coreBottomBar />
|
||||
<coreBottomBar :line="sale.line" :dateData="dateData" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -28,11 +28,11 @@ export default {
|
||||
components: { Container, topItem, coreBottomBar },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
sale: { // 接收父组件传递的设备数据数组
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
dateData: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
@@ -43,135 +43,12 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -12,9 +12,16 @@ export default {
|
||||
props: {
|
||||
chartSeries: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
}
|
||||
},
|
||||
xAxisData: {
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -61,6 +68,7 @@ export default {
|
||||
// 单独提取更新图表的方法,方便复用
|
||||
updateChart() {
|
||||
if (!this.myChart) return;
|
||||
console.log('this.chartSeries', this.chartSeries,this.xAxisData);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
@@ -85,10 +93,28 @@ export default {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
// width: 38,
|
||||
overflow: 'break',
|
||||
formatter: (value) => {
|
||||
const dateParts = value.split('-'); // ["2025", "07", "01"]
|
||||
if (dateParts.length < 2) return value; // 处理异常格式
|
||||
|
||||
switch (this.dateData.mode) {
|
||||
case 1: // 日模式,显示“月-日”
|
||||
// 确保有日的部分
|
||||
return dateParts.length >= 3
|
||||
? `${dateParts[1]}月${dateParts[2]}日`
|
||||
: `${dateParts[1]}月`; // fallback
|
||||
case 2: // 月模式,显示“月”
|
||||
return `${dateParts[1]}月`;
|
||||
case 3: // 年模式,显示“年”
|
||||
return `${dateParts[0]}年`;
|
||||
default: // 默认月模式
|
||||
return `${dateParts[1]}月`;
|
||||
}
|
||||
}
|
||||
},
|
||||
data: ['6月', '7月', '8月', '9月', '10月', '11月']
|
||||
data: this.xAxisData
|
||||
}
|
||||
],
|
||||
yAxis: {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="coreItem">
|
||||
<!-- 动态生成每个 item -->
|
||||
<div class="item" v-for="(item, index) in itemList" :key="index">
|
||||
<div class="unit">{{ item.unit }}</div>
|
||||
<div class="unit">{{ item.name }}</div>
|
||||
<div class="item-content">
|
||||
<!-- 左右内容容器 -->
|
||||
<div class="content-wrapper">
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="right">
|
||||
<!-- 环比额:计算差值并动态绑定颜色 -->
|
||||
<div class="number" :style="{ color: getColor(item.currentValue, item.targetValue) }">
|
||||
{{ get环比额(item.currentValue, item.targetValue) }}
|
||||
{{ item.hbe }}
|
||||
</div>
|
||||
<div class="title">环比额</div>
|
||||
</div>
|
||||
@@ -37,46 +37,75 @@
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
props: ["name", "size", "icon"],
|
||||
props: {
|
||||
cost: { // 接收父组件传递的 cost 数据对象
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
itemList: [
|
||||
{
|
||||
unit: "管理费用·万元",
|
||||
targetValue: 16, // 上月值
|
||||
currentValue: 14.5, // 本月值(小于上月,应显示橙色)
|
||||
},
|
||||
{
|
||||
unit: "销售费用·万元",
|
||||
targetValue: 16,
|
||||
currentValue: 17, // 大于上月,应显示绿色
|
||||
},
|
||||
{
|
||||
unit: "财务费用·万元",
|
||||
targetValue: 16,
|
||||
currentValue: 16, // 等于上月,应显示绿色
|
||||
},
|
||||
]
|
||||
itemList: [] // 初始化为空数组,等待数据加载
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听 cost 数据变化,实时更新 itemList
|
||||
cost: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.itemList = this.transformData(newVal);
|
||||
}
|
||||
},
|
||||
immediate: true, // 组件初始化时立即执行一次
|
||||
deep: true // 深度监听对象内部属性变化
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 核心转换函数:将 cost 对象转换为 itemList 数组
|
||||
* @param {Object} rawData - 原始的 cost 数据对象
|
||||
* @returns {Array} - 转换后的 itemList 数组
|
||||
*/
|
||||
transformData(rawData) {
|
||||
// 定义费用类型映射关系(键名、显示名称、单位)
|
||||
const costMapping = [
|
||||
{ key: 'manageCost', name: '管理费用·万元' },
|
||||
{ key: 'saleCost', name: '销售费用·万元' },
|
||||
{ key: 'financeCost', name: '财务费用·万元' }
|
||||
];
|
||||
|
||||
// 遍历映射关系,转换数据
|
||||
return costMapping.map(mappingItem => {
|
||||
// 获取对应费用类型的数据,若不存在则使用默认值
|
||||
const costData = rawData[mappingItem.key] || { last: 0, this: 0, hbe: 0 };
|
||||
|
||||
return {
|
||||
name: mappingItem.name,
|
||||
targetValue: costData.last, // 上月值
|
||||
currentValue: costData.this // 本月值
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
// 颜色判断:本月 >= 上月 绿色,否则 橙色
|
||||
getColor(current, target) {
|
||||
return current >= target
|
||||
? "rgba(98, 213, 180, 1)"
|
||||
: "rgba(249, 164, 74, 1)";
|
||||
? "rgba(98, 213, 180, 1)" // 绿色:增长或持平
|
||||
: "rgba(249, 164, 74, 1)"; // 橙色:下降
|
||||
},
|
||||
|
||||
// 计算环比额(本月 - 上月),保留一位小数
|
||||
get环比额(current, target) {
|
||||
const diff = current - target;
|
||||
// 正数加"+"号,负数和零保持原样
|
||||
return diff > 0 ? `${diff.toFixed(1)}` : diff.toFixed(1);
|
||||
}
|
||||
// getData(current, target) {
|
||||
// const diff = current - target;
|
||||
// // 正数加"+"号,负数和零保持原样
|
||||
// return diff > 0 ? `+${diff.toFixed(1)}` : diff.toFixed(1);
|
||||
// }
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 样式保持不变 */
|
||||
.coreItem {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
<!-- 根据activeTab状态,切换显示采购/存货内容 -->
|
||||
<template v-if="activeTab === 'purchase'">
|
||||
<!-- 采购重点指标对应的内容 -->
|
||||
<coreBottomLeftItem></coreBottomLeftItem>
|
||||
<coreBottomLeftItem :dateData="dateData" :finance="finance"></coreBottomLeftItem>
|
||||
<div class="bottom"
|
||||
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
<coreBottomBar></coreBottomBar>
|
||||
<coreBottomBar :dateData="dateData" :line="finance.line"></coreBottomBar>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="activeTab === 'inventory'">
|
||||
<!-- 存货重点指标对应的内容 -->
|
||||
<costItem></costItem>
|
||||
<costItem :cost="cost"></costItem>
|
||||
<div class="bottom"
|
||||
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
<CostsBottomBar>
|
||||
<CostsBottomBar :line="cost.line" :dateData="dateData">
|
||||
</CostsBottomBar>
|
||||
</div>
|
||||
</template>
|
||||
@@ -41,14 +41,18 @@ export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, coreBottomLeftItem, coreBottomBar, costItem, CostsBottomBar },
|
||||
props: {
|
||||
leftEqInfoData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
finance: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
productionOverviewVo: {
|
||||
cost: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="lineBottom" style="height: 210px; width: 100%">
|
||||
<!-- 传递当前选中的 series 数据给子组件 -->
|
||||
<coreLineChart style="height: 210px; width: 680px" :chart-series="currentSeries"
|
||||
:key="activeButton + JSON.stringify(currentSeries)" />
|
||||
<!-- 传递当前选中的 series 数据和 xAxis 数据给子组件 -->
|
||||
<coreLineChart style="height: 210px; width: 680px" :chart-series="currentSeries" :x-axis-data="xAxisData"
|
||||
:dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -44,197 +44,93 @@ import * as echarts from 'echarts';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: { coreLineChart },
|
||||
props: ["name", "size", "icon"],
|
||||
props: {
|
||||
line: { // 接收父组件传递的 line 数据
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
dateData: { // 接收父组件传递的 line 数据
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeButton: 0, // 初始激活第一个按钮(索引0)
|
||||
itemList: [
|
||||
{ unit: "单价·元/m²", targetValue: 16, currentValue: 14.5, progress: 90 },
|
||||
{ unit: "净价·元/m²", targetValue: 16, currentValue: 15.2, progress: 85 },
|
||||
{ unit: "销量·万m²", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "双镀面板·万m²", targetValue: 15, currentValue: 13.8, progress: 92 },
|
||||
],
|
||||
// 存储4个按钮对应的 series 数据(每个按钮对应「目标+实际」两条线)
|
||||
seriesMap: [
|
||||
// 0: 营业收入
|
||||
[
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(91, 230, 190, 1)',
|
||||
borderColor: 'rgba(91, 230, 190, 1)',
|
||||
borderWidth: 2
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [500, 620, 580, 720, 650, 800] // 6-11月目标数据
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: 'rgba(255, 132, 0, 1)',
|
||||
borderWidth: 2
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [480, 590, 610, 680, 700, 750] // 6-11月实际数据
|
||||
}
|
||||
],
|
||||
// 1: 经营性利润
|
||||
[
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(91, 230, 190, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [150, 180, 160, 200, 190, 220]
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [140, 170, 180, 190, 210, 200]
|
||||
}
|
||||
],
|
||||
// 2: 利润总额
|
||||
[
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(91, 230, 190, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [120, 150, 140, 170, 160, 190]
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [110, 140, 150, 160, 180, 170]
|
||||
}
|
||||
],
|
||||
// 3: 毛利率(百分比数据)
|
||||
[
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(91, 230, 190, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [35, 36, 35.5, 37, 36.5, 38]
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)' },
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: [34, 35.5, 36, 36.2, 37, 37.5]
|
||||
}
|
||||
]
|
||||
// 定义按钮与 line 数据中 key 的映射关系
|
||||
buttonToDataKey: [
|
||||
'营业收入',
|
||||
'经营收入', // 注意:数据中的 key 是“经营收入”,按钮显示的是“经营性利润”
|
||||
'利润总额',
|
||||
'毛利率'
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 根据当前激活的按钮,返回对应的 series 数据
|
||||
// 根据当前激活的按钮,动态生成对应的 series 数据
|
||||
currentSeries() {
|
||||
return this.seriesMap[this.activeButton] || [];
|
||||
|
||||
const dataKey = this.buttonToDataKey[this.activeButton];
|
||||
const chartData = this.line[dataKey];
|
||||
console.log('this.line[dataKey', this.buttonToDataKey[this.activeButton]);
|
||||
|
||||
if (!chartData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 提取目标和实际数据的值
|
||||
const targetDataValues = Object.values(chartData.target || {});
|
||||
const realDataValues = Object.values(chartData.real || {});
|
||||
console.log('realDataValues', realDataValues);
|
||||
|
||||
return [
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(91, 230, 190, 1)' },
|
||||
itemStyle: { color: 'rgba(91, 230, 190, 1)', borderColor: 'rgba(91, 230, 190, 1)', borderWidth: 2 },
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
|
||||
]),
|
||||
},
|
||||
data: targetDataValues
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
lineStyle: { color: 'rgba(255, 132, 0, 1)' },
|
||||
itemStyle: { color: 'rgba(255, 132, 0, 1)', borderColor: 'rgba(255, 132, 0, 1)', borderWidth: 2 },
|
||||
areaStyle: {
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
data: realDataValues
|
||||
}
|
||||
];
|
||||
},
|
||||
// 提取 x 轴数据(日期)
|
||||
xAxisData() {
|
||||
const dataKey = this.buttonToDataKey[this.activeButton];
|
||||
const chartData = this.line[dataKey];
|
||||
// 使用 'target' 的键作为 x 轴,如果 'target' 不存在,则使用 'real' 的键
|
||||
if (chartData && chartData.target) {
|
||||
return Object.keys(chartData.target);
|
||||
} else if (chartData && chartData.real) {
|
||||
return Object.keys(chartData.real);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div class="top" style="display: flex; width: 100%;">
|
||||
<top-item />
|
||||
<top-item :rawItemList='productData' :dateData="dateData" />
|
||||
</div>
|
||||
<div class="bottom" style="display: flex;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- <top-item /> -->
|
||||
<coreBottomBar />
|
||||
<coreBottomBar :lineData="productData.line" :dateData="dateData" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -27,14 +27,14 @@ export default {
|
||||
components: { Container, topItem, coreBottomBar },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
productData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => { } // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -42,12 +42,6 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
// this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container name="重点工作/三大攻坚战" icon="cockpitItemIcon" size="bottomBasic" topSize="basic">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content"
|
||||
style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;height: 280px;">
|
||||
<div class="bottom"
|
||||
@@ -16,76 +15,78 @@
|
||||
<span class="legend-text">未完成</span>
|
||||
</div>
|
||||
</div>
|
||||
<base-table style="height: 180px;" :page="1" :limit="10" :show-index="true" :beilv="1" :tableConfig="tableProps"
|
||||
:table-data="maintenanceTasks" />
|
||||
|
||||
<base-table style="height: 180px;" :page="1" :limit="10" :show-index="true" :beilv="1"
|
||||
:tableConfig="tableProps" :table-data="tableData" />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
// import * as echarts from 'echarts'
|
||||
import topItem from './top-product-item.vue'
|
||||
import coreBottomBar from './productBottomBar.vue'
|
||||
import baseTable from './baseTable.vue'
|
||||
import finishDiv from './finishDiv.vue'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, topItem, coreBottomBar, baseTable },
|
||||
// mixins: [resize],
|
||||
components: { Container, baseTable },
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
importantWork: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
maintenanceTasks: [
|
||||
{ id: 1, eqName: '研发经费入强度/%', taskName: '例行维护', monthlyActual: '85%', accumulated: '78%', status: 'done' }, // 已完成-绿色
|
||||
{ id: 2, eqName: '存货/亿元', taskName: '例行维护', monthlyActual: '60%', accumulated: '65%', status: 'pending' }, // 未完成-橙色
|
||||
{ id: 3, eqName: '三年以上应收款/亿元', taskName: '故障排查', monthlyActual: '100%', accumulated: '92%', status: 'done' },
|
||||
{ id: 4, eqName: '非经营性资产处置到账金额/万元', taskName: '部件更换', monthlyActual: '45%', accumulated: '50%', status: 'pending' },
|
||||
{ id: 4, eqName: '研发经费投入/万元', taskName: '部件更换', monthlyActual: '45%', accumulated: '50%', status: 'pending' },
|
||||
{ id: 4, eqName: '经营性现金流/万元', taskName: '部件更换', monthlyActual: '45%', accumulated: '50%', status: 'pending' },
|
||||
|
||||
// { id: 2, eqName: '螺杆挤出', taskName: '例行维护', },
|
||||
|
||||
],
|
||||
tableData: [],
|
||||
tableProps: [
|
||||
// { prop: 'id', label: '序号', width: 50, align: 'center' },
|
||||
{ prop: 'eqName', label: '攻坚指标', align: 'center' },
|
||||
{ prop: 'taskName', label: '攻坚目标', align: 'center' },
|
||||
{ prop: 'taskName', label: '当月实际', align: 'center' },
|
||||
{ prop: 'taskName', label: '累计', align: 'center', subcomponent: finishDiv },
|
||||
|
||||
{ prop: 'name', label: '攻坚指标', align: 'center' },
|
||||
{ prop: 'target', label: '攻坚目标', align: 'center' },
|
||||
{ prop: 'monthlyActual', label: '当月实际', align: 'center' },
|
||||
{ prop: 'accumulated', label: '累计', align: 'center', subcomponent: finishDiv },
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
importantWork: {
|
||||
handler(newVal) {
|
||||
this.tableData = this.transformData(newVal);
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
},
|
||||
methods: {
|
||||
transformData(rawData) {
|
||||
if (!rawData || typeof rawData !== 'object') return [];
|
||||
|
||||
const mapping = [
|
||||
{ key: 'jyxxjl', name: '经营现金流', unit: '万元' },
|
||||
{ key: 'yszk', name: '应收账款', unit: '万元' },
|
||||
{ key: 'ch', name: '存货', unit: '万元' },
|
||||
{ key: 'yysr', name: '营业收入', unit: '万元' },
|
||||
{ key: 'snysysk', name: '三年以上应收款', unit: '万元' },
|
||||
{ key: 'dzje', name: '非经营性资产处置到账金额', unit: '万元' },
|
||||
{ key: 'yfjftr', name: '研发经费投入', unit: '万元' },
|
||||
{ key: 'yfjftrqd', name: '研发经费投入强度', unit: '%' }
|
||||
];
|
||||
|
||||
return mapping.map((item, index) => {
|
||||
const data = rawData[item.key] || { monValue: 0, real: 0, target: 0 };
|
||||
const accumulated = data.real || 0;
|
||||
const target = data.target || 0;
|
||||
|
||||
return {
|
||||
id: index + 1,
|
||||
name: item.name + '/' + item.unit,
|
||||
target: target,
|
||||
monthlyActual: data.monValue,
|
||||
accumulated: accumulated,
|
||||
status: accumulated > 0 && target > 0 && accumulated / target >= 1 ? 'done' : 'pending'
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -95,41 +96,31 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
/* 两个图例项之间的间距 */
|
||||
}
|
||||
|
||||
/* 单个图例项 */
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 图例小方块 */
|
||||
.legend-dot {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
border-radius: 2px;
|
||||
/* 可选:轻微圆角 */
|
||||
}
|
||||
|
||||
/* 已完成(绿色) */
|
||||
.legend-dot.done {
|
||||
background-color: #4CAF50;
|
||||
/* 绿色,可根据需求调整色值 */
|
||||
}
|
||||
|
||||
/* 未完成(橙色) */
|
||||
.legend-dot.pending {
|
||||
background-color: #FF9800;
|
||||
/* 橙色,可根据需求调整色值 */
|
||||
}
|
||||
|
||||
/* 图例文字 */
|
||||
.legend-text {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -103,44 +103,40 @@ export default {
|
||||
const mode = this.activeTime + 1; // 1=日,2=月,3=年
|
||||
const defaultMoment = moment(); // 默认当前时间
|
||||
|
||||
// 处理选择的日期:转为moment对象(兼容不同选择器格式)
|
||||
console.log('this.date', this.date);
|
||||
|
||||
const targetMoment = this.date
|
||||
? moment(this.date, this.getPickerType === 'date' ? 'YYYY-MM-DD' : (this.getPickerType === 'month' ? 'YYYY-MM' : 'YYYY'))
|
||||
: defaultMoment;
|
||||
|
||||
// 验证日期有效性
|
||||
if (!targetMoment.isValid()) {
|
||||
console.error('无效日期:', this.date);
|
||||
return { startTime, endTime, mode };
|
||||
}
|
||||
|
||||
// 1. 日维度:当天0点 → 次日0点
|
||||
// 1. 日维度:00:00:00 → 23:59:59(无毫秒)
|
||||
if (this.activeTime === 0) {
|
||||
startTime = targetMoment.startOf('day').valueOf(); // 当天00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'day').startOf('day').valueOf(); // 次日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('day').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('day').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 2. 月维度:当月1日0点 → 次月1日0点
|
||||
// 2. 月维度:当月1日00:00:00 → 当月最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 1) {
|
||||
startTime = targetMoment.startOf('month').valueOf(); // 当月1日00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'month').startOf('month').valueOf(); // 次月1日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('month').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('month').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 3. 年维度:当年1月1日0点 → 次年1月1日0点
|
||||
// 3. 年维度:当年1月1日00:00:00 → 当年最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 2) {
|
||||
startTime = targetMoment.startOf('year').valueOf(); // 当年1月1日00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'year').startOf('year').valueOf(); // 次年1月1日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('year').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('year').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 调试输出(格式化显示,便于验证)
|
||||
// 调试输出:验证是否去掉毫秒
|
||||
console.log('时间范围计算结果:', {
|
||||
mode,
|
||||
startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
startTimeStamp: startTime,
|
||||
endTimeStamp: endTime
|
||||
startTime: moment(startTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 00:00:00
|
||||
endTime: moment(endTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 23:59:59(无毫秒)
|
||||
startTimeStamp: startTime, // 秒级时间戳(如:1764422400)
|
||||
endTimeStamp: endTime // 秒级时间戳(如:1764508799)
|
||||
});
|
||||
|
||||
return { startTime, endTime, mode };
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="lineBottom" style="height: 100%; width: 100%">
|
||||
<operatingLineBar :chartData="chartData" style="height: 99%; width: 100%" />
|
||||
<operatingLineBar :chartData="chartD" style="height: 99%; width: 100%" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -48,18 +48,31 @@ import * as echarts from 'echarts';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: { operatingLineBar },
|
||||
props: ["name", "size", "icon"],
|
||||
props: ["chartData"],
|
||||
data() {
|
||||
return {
|
||||
activeButton: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentDataSource() {
|
||||
console.log('yyyy',this.chartData);
|
||||
|
||||
return this.activeButton === 0 ? this.chartData.sales : this.chartData.grossMargin;
|
||||
},
|
||||
locations() {
|
||||
console.log('this.chartData', this.chartData);
|
||||
|
||||
return this.activeButton === 0 ? this.chartData.salesLocations : this.chartData.grossMarginLocations;
|
||||
},
|
||||
// 根据按钮切换生成对应的 chartData
|
||||
chartData() {
|
||||
chartD() {
|
||||
// 销量场景数据
|
||||
const data = this.currentDataSource;
|
||||
console.log(this.currentDataSource,'currentDataSource');
|
||||
|
||||
const salesData = {
|
||||
allPlaceNames: ['桐城', '合肥', '宜兴', '漳州', '自贡', '洛阳'], // x轴刻度
|
||||
allPlaceNames: this.locations,
|
||||
series: [
|
||||
// 1. 完成率(折线图)
|
||||
{
|
||||
@@ -83,7 +96,7 @@ export default {
|
||||
{ offset: 1, color: 'rgba(40, 138, 255, 0)' }
|
||||
])
|
||||
},
|
||||
data: [104, 96.7, 107.3, 97.1, 107.7, 93.8], // 完成率(%)
|
||||
data: data.rates, // 完成率(%)
|
||||
symbol: 'circle',
|
||||
symbolSize: 6
|
||||
},
|
||||
@@ -105,7 +118,7 @@ export default {
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
borderWidth: 0
|
||||
},
|
||||
data: [50, 60, 55, 70, 65, 80] // 目标销量(万元)
|
||||
data: data.targets // 目标销量(万元)
|
||||
},
|
||||
// 3. 实际(柱状图,含达标状态)
|
||||
{
|
||||
@@ -116,7 +129,7 @@ export default {
|
||||
itemStyle: {
|
||||
color: (params) => {
|
||||
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
||||
const safeFlag = [1, 0, 1, 0, 1, 0];
|
||||
const safeFlag = data.flags;
|
||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||
return currentFlag === 1
|
||||
? {
|
||||
@@ -139,14 +152,13 @@ export default {
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
borderWidth: 0
|
||||
},
|
||||
data: [52, 58, 59, 68, 70, 75] // 实际销量(万元)
|
||||
data: data.reals // 实际销量(万元)
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 毛利率场景数据
|
||||
const grossProfitData = {
|
||||
allPlaceNames: ['1月', '2月', '3月', '4月', '5月', '6月'],
|
||||
series: [
|
||||
// 1. 完成率(折线图)
|
||||
{
|
||||
|
||||
@@ -111,15 +111,16 @@ export default {
|
||||
// 左侧Y轴:营业收入、成本(单位万元)
|
||||
{
|
||||
type: 'value',
|
||||
splitNumber: 4,
|
||||
name: '万元',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
min: 0,
|
||||
max: (value) => Math.ceil((value.max || 0) * 1.1),
|
||||
scale: false,
|
||||
// min: 0,
|
||||
// max: (value) => Math.ceil((value.max || 0) * 1.1),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
@@ -138,8 +139,10 @@ export default {
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
min: 0,
|
||||
max: 100,
|
||||
// min: 0,
|
||||
// max: 100,
|
||||
scale:true,
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
|
||||
@@ -117,9 +117,8 @@ export default {
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
min: 0,
|
||||
max: (value) => Math.ceil((value.max || 0) * 1.1),
|
||||
scale: false,
|
||||
scale: true,
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
|
||||
@@ -16,13 +16,11 @@ export default {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
series: [],
|
||||
allPlaceNames: []
|
||||
}),
|
||||
// 校验数据格式
|
||||
validator: (value) => {
|
||||
return Array.isArray(value.series) && Array.isArray(value.allPlaceNames);
|
||||
}
|
||||
// validator: (value) => {
|
||||
// return Array.isArray(value.series) && Array.isArray(value.allPlaceNames);
|
||||
// }
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -37,7 +35,6 @@ export default {
|
||||
chartData: {
|
||||
handler() {
|
||||
console.log(this.chartData,'chartData');
|
||||
|
||||
this.updateChart();
|
||||
},
|
||||
deep: true,
|
||||
@@ -58,6 +55,7 @@ export default {
|
||||
|
||||
this.myChart = echarts.init(chartDom);
|
||||
const { allPlaceNames, series } = this.chartData || {};
|
||||
console.log('chartData', this.chartData);
|
||||
|
||||
// 处理空数据
|
||||
const xData = allPlaceNames || [];
|
||||
@@ -117,9 +115,8 @@ export default {
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
min: 0,
|
||||
max: (value) => Math.ceil((value.max || 0) * 1.1),
|
||||
scale: false,
|
||||
scale: true,
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
@@ -128,7 +125,6 @@ export default {
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
},
|
||||
// 右侧Y轴:利润占比(百分比)
|
||||
{
|
||||
@@ -148,7 +144,8 @@ export default {
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
// scale: true,
|
||||
splitNumber: 4,
|
||||
}
|
||||
],
|
||||
series: chartSeries // 直接使用父组件传递的 series
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
<Container name="趋势图" icon="cockpitItemIcon" size="operatingLarge" topSize="large">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;width: 100%;">
|
||||
<div class="bottom"
|
||||
style="height: 380px; display: flex; width: 100%;background-color: rgba(249, 252, 255, 1);">
|
||||
<div class="bottom" style="height: 380px; display: flex; width: 100%;background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- <top-item /> -->
|
||||
<coreBottomBar />
|
||||
<coreBottomBar :chartData="chartData" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -15,157 +14,124 @@
|
||||
</template>
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
// import * as echarts from 'echarts'
|
||||
import coreBottomBar from './operatingBar.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, coreBottomBar },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
salesTrendMap: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
grossMarginTrendMap: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
chartData: null // 初始化 chartData 为 null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
grossMarginTrendMap: {
|
||||
handler() {
|
||||
this.processChartData();
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
salesTrendMap: {
|
||||
handler() {
|
||||
this.processChartData();
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
/**
|
||||
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
||||
*/
|
||||
processChartData() {
|
||||
// 关键改动:增加数据有效性检查
|
||||
// 检查 salesTrendMap 是否有实际数据(不只是空对象)
|
||||
const isSalesDataReady = Object.keys(this.salesTrendMap).length > 0;
|
||||
// 检查 grossMarginTrendMap 是否有实际数据
|
||||
const isGrossMarginDataReady = Object.keys(this.grossMarginTrendMap).length > 0;
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
// 如果任一数据未准备好,则不更新 chartData,或传递一个加载中的状态
|
||||
// 你可以根据业务需求调整这里的逻辑,比如:
|
||||
// 1. 等待两者都准备好
|
||||
// 2. 只要有一个准备好了就更新,但确保另一个有合理的默认值
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
// --- 方案一:等待两者都准备好 ---
|
||||
// if (!isSalesDataReady || !isGrossMarginDataReady) {
|
||||
// console.log('数据尚未全部准备好,暂不更新图表');
|
||||
// this.chartData = {
|
||||
// grossMarginLocations: [],
|
||||
// salesLocations: [],
|
||||
// grossMargin: { rates: [], reals: [], targets: [], flags: [] },
|
||||
// sales: { rates: [], reals: [], targets: [], flags: [] },
|
||||
// };
|
||||
// return;
|
||||
// }
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
}
|
||||
// --- 方案二 (推荐):有什么数据就显示什么,没有的就显示空 ---
|
||||
// 这种方式更友好,用户可以先看到部分数据
|
||||
const grossMarginLocations = isGrossMarginDataReady ? Object.keys(this.grossMarginTrendMap) : [];
|
||||
const salesLocations = isSalesDataReady ? Object.keys(this.salesTrendMap) : [];
|
||||
|
||||
this.chart.setOption(option)
|
||||
const processedGrossMarginData = isGrossMarginDataReady
|
||||
? this.processSingleDataset(grossMarginLocations, this.grossMarginTrendMap)
|
||||
: { rates: [], reals: [], targets: [], flags: [] };
|
||||
|
||||
const processedSalesData = isSalesDataReady
|
||||
? this.processSingleDataset(salesLocations, this.salesTrendMap)
|
||||
: { rates: [], reals: [], targets: [], flags: [] };
|
||||
|
||||
// 3. 组装最终的 chartData 对象
|
||||
this.chartData = {
|
||||
grossMarginLocations: grossMarginLocations,
|
||||
salesLocations: salesLocations,
|
||||
grossMargin: processedGrossMarginData,
|
||||
sales: processedSalesData
|
||||
};
|
||||
|
||||
console.log('chartData 已更新:', this.chartData);
|
||||
},
|
||||
|
||||
/**
|
||||
* 通用数据处理函数(纯函数)
|
||||
* @param {Array} locations - 某个指标的地点数组
|
||||
* @param {Object} dataMap - 该指标的原始数据映射
|
||||
* @returns {Object} - 格式化后的数据对象
|
||||
*/
|
||||
processSingleDataset(locations, dataMap) {
|
||||
const rates = [];
|
||||
const reals = [];
|
||||
const targets = [];
|
||||
const flags = [];
|
||||
|
||||
locations.forEach(location => {
|
||||
const data = dataMap[location] || {};
|
||||
// 优化:处理 data.rate 为 null/undefined 的情况
|
||||
const rate = data.rate !== null && data.rate !== undefined ? Math.round(data.rate * 100) : 0;
|
||||
|
||||
rates.push(rate);
|
||||
reals.push(data.real ?? 0); // 使用空值合并运算符
|
||||
targets.push(data.target ?? 0);
|
||||
|
||||
// 优化:更清晰的逻辑
|
||||
if (data.target === 0) {
|
||||
flags.push(1); // 如果目标为0,默认达标
|
||||
} else {
|
||||
flags.push(rate >= 100 ? 1 : 0);
|
||||
}
|
||||
});
|
||||
|
||||
return { rates, reals, targets, flags };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="kpi-content" style="padding: 14px 14px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div class="top" style="display: flex; width: 100%;">
|
||||
<top-item :height="367" :itemList="parentItemList" />
|
||||
<top-item v-if="saleData" :height="367" :itemList="formattedParentItemList" />
|
||||
</div>
|
||||
<div class="bottom"
|
||||
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
@@ -27,14 +27,10 @@ export default {
|
||||
components: { Container, topItem },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
saleData: { // 接收父组件传递的设备数据数组
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -42,57 +38,57 @@ export default {
|
||||
parentItemList: [
|
||||
{
|
||||
name: "利润总额",
|
||||
targetValue: 50,
|
||||
value: 58,
|
||||
proportion: 116,
|
||||
targetValue: 0,
|
||||
value: 0,
|
||||
proportion: 0,
|
||||
route: 'profitAnalysis',
|
||||
completed: 1 // 实际超目标,达标
|
||||
},
|
||||
{
|
||||
name: "毛利率",
|
||||
targetValue: 30,
|
||||
value: 28.5,
|
||||
proportion: 95,
|
||||
targetValue: 0,
|
||||
value: 0,
|
||||
proportion: 0,
|
||||
route: 'profitAnalysis',
|
||||
completed: 0 // 未达30%目标,不达标
|
||||
completed: 1 // 未达30%目标,不达标
|
||||
},
|
||||
{
|
||||
name: "单价",
|
||||
targetValue: 12,
|
||||
value: 12.5,
|
||||
proportion: 104.2,
|
||||
targetValue: 0,
|
||||
value: 0,
|
||||
proportion: 0,
|
||||
route: 'cost/cost',
|
||||
completed: 1 // 单价达标
|
||||
},
|
||||
{
|
||||
name: "净价",
|
||||
targetValue: 9,
|
||||
value: 8.8,
|
||||
proportion: 97.8,
|
||||
targetValue: 0,
|
||||
value: 0,
|
||||
proportion: 0,
|
||||
route: 'cost/cost',
|
||||
completed: 0 // 未达目标,不达标
|
||||
completed: 1 // 未达目标,不达标
|
||||
},
|
||||
{
|
||||
name: "销量",
|
||||
targetValue: 100,
|
||||
value: 120,
|
||||
proportion: 120,
|
||||
targetValue: 0,
|
||||
value: 0,
|
||||
proportion: 0,
|
||||
route: 'profitAnalysis',
|
||||
completed: 1 // 销量超额达标
|
||||
},
|
||||
{
|
||||
name: "双镀面板",
|
||||
targetValue: 30,
|
||||
value: 29,
|
||||
proportion: 96.7,
|
||||
targetValue: 0,
|
||||
value: 0,
|
||||
proportion: 0,
|
||||
route: 'profitAnalysis',
|
||||
completed: 0 // 略低目标,不达标
|
||||
completed: 1 // 略低目标,不达标
|
||||
},
|
||||
{
|
||||
name: "溢价产品销量",
|
||||
targetValue: 15,
|
||||
value: 18,
|
||||
proportion: 120,
|
||||
targetValue: 0,
|
||||
value: 0,
|
||||
proportion: 0,
|
||||
route: 'profitAnalysis',
|
||||
completed: 1 // 超额达标
|
||||
}
|
||||
@@ -100,135 +96,59 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedParentItemList() {
|
||||
// --- 新增判断 ---
|
||||
// 如果 saleData 不存在、为 null,或者是不包含任何属性的空对象,则返回原始的 parentItemList
|
||||
if (!this.saleData || Object.keys(this.saleData).length === 0) {
|
||||
return this.parentItemList;
|
||||
}
|
||||
// --- 判断结束 ---
|
||||
|
||||
// 定义一个名称到键的映射表,方便查找
|
||||
const nameToKeyMap = {
|
||||
"利润总额": "totalProfit",
|
||||
"毛利率": "grossMargin",
|
||||
"单价": "unitPrice",
|
||||
"净价": "netPrice",
|
||||
"销量": "sales",
|
||||
"双镀面板": "panel",
|
||||
"溢价产品销量": "premiumProduct"
|
||||
};
|
||||
|
||||
// 遍历原始的 parentItemList
|
||||
return this.parentItemList.map(item => {
|
||||
// 根据当前 item 的 name 找到 SaleData 中对应的键
|
||||
const key = nameToKeyMap[item.name];
|
||||
|
||||
// 如果找到了对应的键,并且 saleItem 存在,就从 SaleData 中获取数据
|
||||
if (key && this.saleData[key]) {
|
||||
const saleItem = this.saleData[key];
|
||||
return {
|
||||
...item,
|
||||
value: saleItem.real,
|
||||
targetValue: saleItem.target,
|
||||
// proportion: saleItem.rate !== null && saleItem.rate !== undefined
|
||||
// ? Math.round(saleItem.rate * 100)
|
||||
// : 0,
|
||||
// 直接使用处理好的 rate
|
||||
proportion: saleItem.rate,
|
||||
// 根据完成率判断是否达标 (假设 >=100% 为达标)
|
||||
completed: saleItem.rate >= 1 ? 1 : 0,
|
||||
};
|
||||
}
|
||||
|
||||
// 如果没有找到对应的数据,则返回原始 item
|
||||
return item;
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -391,14 +311,4 @@ export default {
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
<!-- 动态渲染城市进度:循环 item.cities -->
|
||||
<div class="right-city" v-for="(city, cityIdx) in item.cities" :key="cityIdx"
|
||||
:style="{ marginTop: cityIdx > 0 ? '2px' : '0' }">
|
||||
:style="{ marginTop: cityIdx > 0 ? '2px' : '0' }" @click="getTableData(city.num)" style="cursor: pointer;">
|
||||
<div class="city">{{ city.name }}</div> <!-- 动态城市名 -->
|
||||
<div class="city-progress-group">
|
||||
<div class="city-progress-container">
|
||||
@@ -102,72 +102,145 @@
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
props: ["name", "size", "icon"],
|
||||
props: ["orderOutput"],
|
||||
data() {
|
||||
return {
|
||||
progress: 90, // 进度值基础参数
|
||||
itemList: [
|
||||
// {
|
||||
// unit: "总进度",
|
||||
// targetValue: 16,
|
||||
// currentValue: 14.5,
|
||||
// progress: 90,
|
||||
// cities: [] // 总进度无需城市数据,留空
|
||||
// },
|
||||
// {
|
||||
// unit: "一组",
|
||||
// targetValue: 16,
|
||||
// currentValue: 17,
|
||||
// progress: 106,
|
||||
// cities: [
|
||||
// { name: "桐城", completed: 12, total: 13, progress: 92 },
|
||||
// { name: "自贡", completed: 15, total: 16, progress: 93 } // 新增城市示例
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// unit: "二组",
|
||||
// targetValue: 16,
|
||||
// currentValue: 16,
|
||||
// progress: 100,
|
||||
// cities: [
|
||||
// { name: "蚌埠", completed: 10, total: 12, progress: 83 },
|
||||
// { name: "合肥", completed: 8, total: 10, progress: 80 }
|
||||
// ]
|
||||
// },
|
||||
// // 其他组同理,按需添加 cities 数据
|
||||
// {
|
||||
// unit: "三组",
|
||||
// targetValue: 16,
|
||||
// currentValue: 15.2,
|
||||
// progress: 85,
|
||||
// cities: [{ name: "宜兴", completed: 9, total: 11, progress: 81 }]
|
||||
// },
|
||||
// {
|
||||
// unit: "四组",
|
||||
// targetValue: 16,
|
||||
// currentValue: 18,
|
||||
// progress: 112,
|
||||
// cities: [
|
||||
// { name: "漳州", completed: 14, total: 15, progress: 93 },
|
||||
// { name: "洛阳", completed: 12, total: 14, progress: 85 }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// unit: "五组",
|
||||
// targetValue: 16,
|
||||
// currentValue: 14,
|
||||
// progress: 80,
|
||||
// cities: [{ name: "桐城", completed: 7, total: 9, progress: 77 }]
|
||||
// }
|
||||
]
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
orderOutput: {
|
||||
handler(newValue, oldValue) {
|
||||
this.getItemData(newValue)
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getItemData(data) {
|
||||
this.itemList = [
|
||||
{
|
||||
unit: "总进度",
|
||||
targetValue: 16,
|
||||
currentValue: 14.5,
|
||||
progress: 90,
|
||||
targetValue: data.totalProgress.target,
|
||||
currentValue: data.totalProgress.real,
|
||||
progress: data.totalProgress.rate,
|
||||
cities: [] // 总进度无需城市数据,留空
|
||||
},
|
||||
{
|
||||
unit: "一组",
|
||||
targetValue: 16,
|
||||
currentValue: 17,
|
||||
progress: 106,
|
||||
targetValue: data.group1.target,
|
||||
currentValue: data.group1.real,
|
||||
progress: data.group1.rate,
|
||||
cities: [
|
||||
{ name: "桐城", completed: 12, total: 13, progress: 92 },
|
||||
{ name: "自贡", completed: 15, total: 16, progress: 93 } // 新增城市示例
|
||||
{ name: "桐城", completed: data[2].real, total: data[2].target, progress: data[2].rate,num:2 },
|
||||
{ name: "自贡", completed: data[3].real, total: data[3].target, progress: data[3].rate, num: 3 } // 新增城市示例
|
||||
]
|
||||
},
|
||||
{
|
||||
unit: "二组",
|
||||
targetValue: 16,
|
||||
currentValue: 16,
|
||||
progress: 100,
|
||||
targetValue: data.group2.target,
|
||||
currentValue: data.group2.real,
|
||||
progress: data.group2.rate,
|
||||
cities: [
|
||||
{ name: "蚌埠", completed: 10, total: 12, progress: 83 },
|
||||
{ name: "合肥", completed: 8, total: 10, progress: 80 }
|
||||
{ name: "蚌埠", completed: data[4].real, total: data[4].target, progress: data[4].rate, num: 4 },
|
||||
{ name: "合肥", completed: data[5].real, total: data[5].target, progress: data[5].rate, num: 5 }
|
||||
]
|
||||
},
|
||||
// 其他组同理,按需添加 cities 数据
|
||||
{
|
||||
unit: "三组",
|
||||
targetValue: 16,
|
||||
currentValue: 15.2,
|
||||
progress: 85,
|
||||
cities: [{ name: "宜兴", completed: 9, total: 11, progress: 81 }]
|
||||
targetValue: data.group3.target,
|
||||
currentValue: data.group3.real,
|
||||
progress: data.group3.rate,
|
||||
cities: [{ name: "江苏凯盛", completed: data[6].real, total: data[6].target, progress: data[6].rate, num: 6 },
|
||||
{ name: "宜兴", completed: data[7].real, total: data[7].target, progress: data[7].rate, num: 7 }
|
||||
]
|
||||
},
|
||||
{
|
||||
unit: "四组",
|
||||
targetValue: 16,
|
||||
currentValue: 18,
|
||||
progress: 112,
|
||||
targetValue: data.group4.target,
|
||||
currentValue: data.group4.real,
|
||||
progress: data.group4.rate,
|
||||
cities: [
|
||||
{ name: "漳州", completed: 14, total: 15, progress: 93 },
|
||||
{ name: "洛阳", completed: 12, total: 14, progress: 85 }
|
||||
{ name: "漳州", completed: data[8].real, total: data[8].target, progress: data[8].rate, num: 8 },
|
||||
{ name: "洛阳", completed: data[9].real, total: data[9].target, progress: data[9].rate, num: 9 }
|
||||
]
|
||||
},
|
||||
{
|
||||
unit: "五组",
|
||||
targetValue: 16,
|
||||
currentValue: 14,
|
||||
progress: 80,
|
||||
cities: [{ name: "桐城", completed: 7, total: 9, progress: 77 }]
|
||||
targetValue: data.group5.target,
|
||||
currentValue: data.group5.real,
|
||||
progress: data.group5.rate,
|
||||
cities: [{ name: "秦皇岛", completed: data[10].real, total: data[10].target, progress: data[10].rate, num: 10 },
|
||||
// { name: "秦皇岛", completed: 7, total: 9, progress: 77 }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
// 颜色判断核心方法:实际值≥目标值返回绿色,否则返回橙色
|
||||
getColor(currentValue, targetValue) {
|
||||
return currentValue >= targetValue
|
||||
? "rgba(98, 213, 180, 1)"
|
||||
: "rgba(249, 164, 74, 1)";
|
||||
},
|
||||
getTableData(data) {
|
||||
console.log(data, 'data');
|
||||
this.$emit('handleShowTable',data)
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -466,15 +539,16 @@ export default {
|
||||
}
|
||||
|
||||
/* 右上角折现边框(主边框) */
|
||||
.groupData::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 0 100%);
|
||||
}
|
||||
// .groupData::before {
|
||||
// content: "";
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// background-color: #000000;
|
||||
// clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 0 100%);
|
||||
// }
|
||||
|
||||
/* 右上角折现细节 */
|
||||
.groupData::after {
|
||||
|
||||
38
src/views/home/components/orderColor.vue
Normal file
38
src/views/home/components/orderColor.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<!-- 对象语法:根据status的值动态添加类名 -->
|
||||
<div class="accumulated-value" :class="{ pending: injectData.status === 1, done: injectData.status === 2 }">
|
||||
<!-- 同样可添加状态文本(可选) -->
|
||||
{{ injectData.status === 2 ? '已完成' : injectData.status === 1 ? '生产中' : '' }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FinishDiv',
|
||||
props: {
|
||||
injectData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- style部分与方式1完全一致 -->
|
||||
<style scoped>
|
||||
.accumulated-value {
|
||||
font-size: 14px;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.accumulated-value.done {
|
||||
color: #4CAF50;
|
||||
/* background-color: rgba(76, 175, 80, 0.1); */
|
||||
}
|
||||
|
||||
.accumulated-value.pending {
|
||||
color: #FF9800;
|
||||
/* background-color: rgba(255, 152, 0, 0.1); */
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,12 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<bottomMiddleContainer name="订单产量跟踪·万m²" icon="cockpitItemIcon" size="bottomBasic">
|
||||
<bottomMiddleContainer name="订单产量跟踪·万m²" icon="cockpitItemIcon" size="bottomBasic" :tableShow="tableShow"
|
||||
@handleShow="showTable">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div style="display: flex;gap: 9px;padding: 14px 16px;">
|
||||
<orderItem />
|
||||
<orderItem v-show="!tableShow" :orderOutput="orderOutput" @handleShowTable="getTable" />
|
||||
<base-table v-show="tableShow" style="height: 252px;width: 100%;" :page="1" :limit="10" :show-index="true"
|
||||
:beilv="1" :tableConfig="tableProps" :table-data="tableData" />
|
||||
</div>
|
||||
</bottomMiddleContainer>
|
||||
</div>
|
||||
@@ -11,52 +14,56 @@
|
||||
<script>
|
||||
import bottomMiddleContainer from './bottomMiddleContainer.vue'
|
||||
// import * as echarts from 'echarts'
|
||||
import topItem from './top-product-item.vue'
|
||||
import coreBottomBar from './productBottomBar.vue'
|
||||
import orderItem from './order-bottom-leftItem.vue'
|
||||
|
||||
|
||||
import baseTable from './baseTable.vue'
|
||||
import orderColor from './orderColor.vue'
|
||||
import proColor from './proColor.vue'
|
||||
import moment from 'moment'
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { bottomMiddleContainer, topItem, coreBottomBar, orderItem },
|
||||
components: { bottomMiddleContainer, orderItem, baseTable },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
orderOutput: { // 接收父组件传递的设备数据数组
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
baseOrder: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
maintenanceTasks: [
|
||||
{ id: 1, eqName: '连续化', taskName: '例行维护', },
|
||||
{ id: 2, eqName: '螺杆挤出', taskName: '例行维护', },
|
||||
{ id: 2, eqName: '螺杆挤出', taskName: '例行维护', },
|
||||
// { id: 2, eqName: '螺杆挤出', taskName: '例行维护', },
|
||||
// { id: 2, eqName: '螺杆挤出', taskName: '例行维护', },
|
||||
|
||||
],
|
||||
tableData: [],
|
||||
tableShow:false,
|
||||
tableProps: [
|
||||
// { prop: 'id', label: '序号', width: 50, align: 'center' },
|
||||
{ prop: 'eqName', label: '攻坚指标', align: 'left' },
|
||||
{ prop: 'taskName', label: '攻坚目标', align: 'left' },
|
||||
{ prop: 'taskName', label: '当月实际', align: 'left' },
|
||||
{ prop: 'taskName', label: '累计', align: 'left' },
|
||||
|
||||
{ prop: 'code', label: '订单编号', align: 'center' },
|
||||
{ prop: 'customerName', label: '客户名称', align: 'center' },
|
||||
{
|
||||
prop: 'deliveryTime', label: '交货时间', align: 'center',
|
||||
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
|
||||
},
|
||||
{ prop: 'spec', label: '规格(mm)', align: 'center', },
|
||||
{ prop: 'num', label: '生产量(平方米)', align: 'center', },
|
||||
{ prop: 'progress', label: '生产进度', align: 'center', subcomponent: proColor },
|
||||
{ prop: 'status', label: '订单状态', align: 'center', subcomponent: orderColor },
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
baseOrder: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
this.tableData = newValue
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
// orderOutput: {
|
||||
// handler(newValue, oldValue) {
|
||||
// this.tableData = newValue
|
||||
// },
|
||||
// deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
// }
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
@@ -66,6 +73,16 @@ export default {
|
||||
// 销毁图表,避免内存泄漏
|
||||
},
|
||||
methods: {
|
||||
showTable(flag) {
|
||||
this.tableShow = flag
|
||||
console.log('this.tableShow', this.tableShow);
|
||||
|
||||
},
|
||||
getTable(num) {
|
||||
this.tableShow = true
|
||||
console.log('num', num);
|
||||
this.$emit('getData', num)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -17,21 +17,35 @@ export default {
|
||||
// // 验证:ref 名不能为空,确保有效
|
||||
// return value.trim() !== '';
|
||||
// }
|
||||
}
|
||||
},
|
||||
pieData: {
|
||||
type: Object,
|
||||
default: () => { } // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
// 监听 pieData 变化,只要数据变了,就更新图表
|
||||
pieData: {
|
||||
handler() {
|
||||
this.initData(); // 直接调用更新,无需判断 myChart 是否存在
|
||||
},
|
||||
deep: true,
|
||||
immediate: true // 初始化时立即执行
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initData();
|
||||
this.initChart(); // 只负责初始化图表实例
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
// 2. 动态获取 DOM:通过 props 中的 chartRef 拿到对应的 ref 元素
|
||||
console.log(this.pieData,'this.pieData.value');
|
||||
|
||||
const chartDom = this.$refs[this.chartRef];
|
||||
if (!chartDom) {
|
||||
console.error(`图表容器未找到!请确认父组件传递的 chartRef 为 "${this.chartRef}"`);
|
||||
@@ -94,7 +108,7 @@ export default {
|
||||
labelLine: {
|
||||
show: true,
|
||||
length: 0,
|
||||
length2: 30,
|
||||
length2: 10,
|
||||
lineStyle: {
|
||||
color: (params) => customColors[params.dataIndex]
|
||||
}
|
||||
@@ -104,7 +118,7 @@ export default {
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: 1048, name: '单镀面板',
|
||||
value: this.pieData?.value || 0, name: '单镀面板',
|
||||
label: {
|
||||
normal: {
|
||||
align: 'left',
|
||||
@@ -133,7 +147,9 @@ export default {
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
lineStyle: { color: 'rgba(39, 96, 255, 1)' }
|
||||
lineStyle: { color: 'rgba(39, 96, 255, 1)' },
|
||||
length: 10,
|
||||
length2: 20,
|
||||
},
|
||||
itemStyle: { color: 'rgba(39, 96, 255, 1)' }
|
||||
},
|
||||
@@ -168,8 +184,8 @@ export default {
|
||||
},
|
||||
labelLine: {
|
||||
length: 0,
|
||||
length2: 50,
|
||||
lineStyle: { color: 'rgba(40, 138, 255, 1)' }
|
||||
length2: 10,
|
||||
lineStyle: { color: 'rgba(40, 138, 255, 1)' },
|
||||
},
|
||||
itemStyle: { color: 'rgba(40, 138, 255, 1)' }
|
||||
},
|
||||
@@ -203,7 +219,9 @@ export default {
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
lineStyle: { color: 'rgba(118, 218, 190, 1)' }
|
||||
lineStyle: { color: 'rgba(118, 218, 190, 1)' },
|
||||
length: 0,
|
||||
length2: 10,
|
||||
},
|
||||
itemStyle: { color: 'rgba(118, 218, 190, 1)' }
|
||||
},
|
||||
@@ -237,7 +255,9 @@ export default {
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
lineStyle: { color: 'rgba(255, 206, 106, 1)' }
|
||||
lineStyle: { color: 'rgba(255, 206, 106, 1)' },
|
||||
length: 10,
|
||||
length2: 10,
|
||||
},
|
||||
itemStyle: { color: 'rgba(255, 206, 106, 1)' }
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="kpi-content" style="padding: 14px 16px 8px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div class="top" style="display: flex; width: 100%;">
|
||||
<top-item :itemList="parentItemList" />
|
||||
<top-item :itemList="formattedPremiumProductList" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom-content" style="display: flex;flex-direction: column; width: 100%;">
|
||||
@@ -28,10 +28,10 @@
|
||||
</div>
|
||||
<div class="pie" style="display: flex;gap: 8px;margin-top: 68px;padding: 0 16px;">
|
||||
<div class="month-pie" style="height: 212px;width: 382px;background: #F9FCFF;">
|
||||
<pieChart :chartRef=" 'monthChart' " />
|
||||
<pieChart :pieData="monthPieData" :chartRef=" 'monthChart' " />
|
||||
</div>
|
||||
<div class="-pie" style="height: 212px;width: 382px;background: #F9FCFF;">
|
||||
<pieChartTwo :chartRef="'yearChart'" />
|
||||
<pieChartTwo :pieData="yearPieData" :chartRef="'yearChart'" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,11 +54,11 @@ export default {
|
||||
components: { Container, topItem, coreBottomBar, pieChart, pieChartTwo },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
premiumProduct: { // 接收父组件传递的设备数据数组
|
||||
type: Object,
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
salesProportion: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
@@ -66,20 +66,22 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
monthPieData: {},
|
||||
yearPieData:{},
|
||||
parentItemList: [
|
||||
{
|
||||
name: "月度",
|
||||
targetValue: 80,
|
||||
value: 76,
|
||||
proportion: 95,
|
||||
targetValue: 0,
|
||||
value: 0,
|
||||
proportion: 0,
|
||||
route: 'profitAnalysis',
|
||||
completed: 0 // 未达目标值,不达标
|
||||
completed: 1 // 未达目标值,不达标
|
||||
},
|
||||
{
|
||||
name: "年度",
|
||||
targetValue: 900,
|
||||
value: 920,
|
||||
proportion: 102.2,
|
||||
targetValue: 0,
|
||||
value: 0,
|
||||
proportion: 0,
|
||||
route: 'profitAnalysis',
|
||||
completed: 1 // 超出目标值,达标
|
||||
}
|
||||
@@ -87,134 +89,68 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
salesProportion: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
console.log('salesProportion',newValue);
|
||||
|
||||
this.getPieData()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedPremiumProductList() {
|
||||
const premiumProductData = this.premiumProduct || {};
|
||||
|
||||
const nameToKeyMap = {
|
||||
"月度": "month",
|
||||
"年度": "year"
|
||||
};
|
||||
|
||||
return this.parentItemList.map(item => {
|
||||
const key = nameToKeyMap[item.name];
|
||||
|
||||
if (key && premiumProductData[key]) {
|
||||
const periodData = premiumProductData[key];
|
||||
|
||||
let completed = 0;
|
||||
|
||||
// 新增:判断三个值是否都为0
|
||||
const allZeros = periodData.real === 0 &&
|
||||
periodData.target === 0 &&
|
||||
periodData.rate === 0;
|
||||
|
||||
if (allZeros) {
|
||||
completed = 1;
|
||||
} else if (periodData.rate !== null && periodData.rate !== undefined) {
|
||||
completed = periodData.rate >= 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
value: periodData.real,
|
||||
targetValue: periodData.target,
|
||||
proportion: periodData.rate !== null && periodData.rate !== undefined
|
||||
? Math.round(periodData.rate * 100)
|
||||
: 0,
|
||||
completed: completed,
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
getPieData() {
|
||||
this.monthPieData = this.salesProportion ? this.salesProportion.month : {}
|
||||
this.yearPieData = this.salesProportion ? this.salesProportion.year : {}
|
||||
console.log('this.monthPieData', this.monthPieData, this.yearPieData);
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,16 +328,3 @@ export default {
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
41
src/views/home/components/proColor.vue
Normal file
41
src/views/home/components/proColor.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<!-- 对象语法:根据status的值动态添加类名 -->
|
||||
<div class="accumulated-value" :class="{
|
||||
pending: injectData.progress < 100, // 进度小于100%时,添加 pending 类
|
||||
done: injectData.progress >= 100
|
||||
}">
|
||||
<!-- 同样可添加状态文本(可选) -->
|
||||
{{ injectData.progress }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FinishDiv',
|
||||
props: {
|
||||
injectData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- style部分与方式1完全一致 -->
|
||||
<style scoped>
|
||||
.accumulated-value {
|
||||
font-size: 14px;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.accumulated-value.done {
|
||||
color: #4CAF50;
|
||||
/* background-color: rgba(76, 175, 80, 0.1); */
|
||||
}
|
||||
|
||||
.accumulated-value.pending {
|
||||
color: #FF9800;
|
||||
/* background-color: rgba(255, 152, 0, 0.1); */
|
||||
}
|
||||
</style>
|
||||
@@ -19,30 +19,74 @@ import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
name: 'Container',
|
||||
props: ["chartData",'dateData'],
|
||||
components: {},
|
||||
data() {
|
||||
return {};
|
||||
return {
|
||||
myChart: null, // 存储 echarts 实例
|
||||
};
|
||||
},
|
||||
// 关键:监听 chartData 变化
|
||||
watch: {
|
||||
chartData: {
|
||||
handler(newData) {
|
||||
this.updateChart(newData);
|
||||
},
|
||||
immediate: true, // 组件初始化时立即执行一次
|
||||
deep: true, // 深度监听对象内部变化
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initData();
|
||||
this.initChart();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
// 优先使用 ref 获取 DOM,避免 id 冲突
|
||||
// 初始化图表实例
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
console.error('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
const myChart = echarts.init(chartDom);
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 初始化时调用一次更新
|
||||
this.updateChart(this.chartData);
|
||||
|
||||
// 监听窗口缩放
|
||||
window.addEventListener('resize', () => {
|
||||
this.myChart?.resize();
|
||||
});
|
||||
},
|
||||
|
||||
// 核心:根据数据更新图表
|
||||
updateChart(data) {
|
||||
if (!this.myChart) {
|
||||
// 如果实例还未初始化,则等待 initChart 完成后再更新
|
||||
setTimeout(() => this.updateChart(data), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 处理数据,如果 data 无效则清空图表
|
||||
if (!data || typeof data !== 'object' || (!data.real && !data.target)) {
|
||||
this.myChart.setOption({
|
||||
xAxis: { data: [] },
|
||||
series: [{ data: [] }, { data: [] }]
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 提取 X 轴数据(从 real 或 target 中取键名)
|
||||
const xAxisData = data.real ? Object.keys(data.real) : Object.keys(data.target);
|
||||
console.log('xAxisData', xAxisData);
|
||||
|
||||
// 3. 提取 "实际" 和 "目标" 系列的数据
|
||||
const realData = data.real ? Object.values(data.real) : [];
|
||||
const targetData = data.target ? Object.values(data.target) : [];
|
||||
|
||||
// 4. 准备 echarts 的 option 配置
|
||||
const option = {
|
||||
// color: ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'],
|
||||
// title: {
|
||||
// text: 'Gradient Stacked Area Chart'
|
||||
// },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
@@ -55,60 +99,56 @@ export default {
|
||||
grid: {
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
// top: 10,
|
||||
// bottom: 20,
|
||||
right: 25,
|
||||
},
|
||||
// legend: {
|
||||
// data: ['Line 1', 'Line 2', 'Line 3', 'Line 4', 'Line 5']
|
||||
// },
|
||||
// toolbox: {
|
||||
// feature: {
|
||||
// saveAsImage: {}
|
||||
// }
|
||||
// },
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
onZero: false,
|
||||
lineStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.15)'
|
||||
}
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
// 这里可以根据需要调整标签的显示方式
|
||||
formatter: (value) => {
|
||||
const dateParts = value.split('-'); // ["2025", "07", "01"]
|
||||
if (dateParts.length < 2) return value; // 处理异常格式
|
||||
|
||||
switch (this.dateData.mode) {
|
||||
case 1: // 日模式,显示“月-日”
|
||||
// 确保有日的部分
|
||||
return dateParts.length >= 3
|
||||
? `${dateParts[1]}月${dateParts[2]}日`
|
||||
: `${dateParts[1]}月`; // fallback
|
||||
case 2: // 月模式,显示“月”
|
||||
return `${dateParts[1]}月`;
|
||||
case 3: // 年模式,显示“年”
|
||||
return `${dateParts[0]}年`;
|
||||
default: // 默认月模式
|
||||
return `${dateParts[1]}月`;
|
||||
}
|
||||
}
|
||||
},
|
||||
data: ['6月', '7月', '8月', '9月', '10月', '11月']
|
||||
data: xAxisData // 使用提取出的 X 轴数据
|
||||
}
|
||||
],
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
// name: '单位/片',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 14,
|
||||
align: 'left'
|
||||
},
|
||||
min: function (value) {
|
||||
return 0
|
||||
},
|
||||
max: function (value) {
|
||||
return Math.ceil(value.max)
|
||||
},
|
||||
min: 0,
|
||||
// max: function (value) { return Math.ceil(value.max * 1.1); }, // 增加一点余量
|
||||
scale: true,
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12
|
||||
@@ -116,101 +156,88 @@ export default {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.15)',
|
||||
// type: 'dashed'
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.15)'
|
||||
}
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '实际',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle', // 点的形状(circle为圆形)
|
||||
// stack: 'Total', // 趋势图通常不需要堆叠
|
||||
symbol: 'circle',
|
||||
symbolSize: 8,
|
||||
lineStyle: {
|
||||
color: 'rgba(255, 132, 0, .5)',
|
||||
color: 'rgba(255, 132, 0, 1)', // 加深颜色
|
||||
width: 2,
|
||||
},
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 132, 0, .5)',
|
||||
borderColor: 'rgba(255, 132, 0, .5)', // 数据点边框色(白色)
|
||||
borderWidth: 2, // 数据点边框宽度
|
||||
color: 'rgba(255, 132, 0, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.5,
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(255, 132, 0, .4)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(18, 255, 245, 0)',
|
||||
},
|
||||
{ offset: 0, color: 'rgba(255, 132, 0, .5)' },
|
||||
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
|
||||
]),
|
||||
},
|
||||
// emphasis: { focus: 'series' },
|
||||
data: [140, 232, 101, 264, 90, 340, 250]
|
||||
data: realData // 使用提取出的 "实际" 数据
|
||||
},
|
||||
{
|
||||
name: '目标',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
symbol: 'circle', // 点的形状(circle为圆形)
|
||||
// stack: 'Total',
|
||||
symbol: 'circle',
|
||||
symbolSize: 8,
|
||||
lineStyle: {
|
||||
color: 'rgba(98, 213, 180, .5)',
|
||||
color: 'rgba(98, 213, 180, 1)', // 加深颜色
|
||||
width: 2,
|
||||
type: 'dashed' // 目标线使用虚线
|
||||
},
|
||||
itemStyle: {
|
||||
color: 'rgba(98, 213, 180, .5)',
|
||||
borderColor: 'rgba(98, 213, 180, .5)', // 数据点边框色(白色)
|
||||
borderWidth: 2, // 数据点边框宽度
|
||||
color: 'rgba(98, 213, 180, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.5,
|
||||
opacity: 0.3,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(98, 213, 180,.4)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(18, 255, 245, 0)',
|
||||
},
|
||||
{ offset: 0, color: 'rgba(98, 213, 180, .5)' },
|
||||
{ offset: 1, color: 'rgba(98, 213, 180, 0)' },
|
||||
]),
|
||||
},
|
||||
// emphasis: { focus: 'series' },
|
||||
data: [120, 282, 111, 234, 220, 340, 310]
|
||||
data: targetData // 使用提取出的 "目标" 数据
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
// 监听窗口缩放
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
|
||||
// 组件销毁时清理
|
||||
this.$once('hook:destroyed', () => {
|
||||
window.removeEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
myChart.dispose();
|
||||
});
|
||||
// 5. 应用配置项更新图表
|
||||
this.myChart.setOption(option, true);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理
|
||||
window.removeEventListener('resize', () => {
|
||||
this.myChart?.resize();
|
||||
});
|
||||
this.myChart?.dispose();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
/* (你的样式代码保持不变) */
|
||||
.legend {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: -5px;
|
||||
display: flex;
|
||||
/* 使用 flex 布局让两个图例项并排且对齐 */
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.legend-item-line {
|
||||
@@ -220,25 +247,27 @@ export default {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
margin-right: 20px;
|
||||
/* 增加两个图例项之间的间距 */
|
||||
position: relative;
|
||||
padding-left: 8px;
|
||||
/* 给文字左侧增加内边距 */
|
||||
padding-left: 20px;
|
||||
/* 为圆点和线条留出空间 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.line {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
/* 调整线条位置 */
|
||||
top: 10px;
|
||||
left: 6px;
|
||||
/* 线条从圆点右侧开始 */
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
width: 16px;
|
||||
/* 线条长度 */
|
||||
height: 2px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.target {
|
||||
background: rgba(91, 230, 190, 1);
|
||||
background: rgba(98, 213, 180, 1);
|
||||
}
|
||||
|
||||
.real {
|
||||
@@ -251,23 +280,18 @@ export default {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
/* 关键:增加图例圆点和文字之间的间距 */
|
||||
margin-bottom: 2px;
|
||||
margin-right: 8px;
|
||||
background-color: rgba(255, 132, 0, 1);
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.legend-item-line:nth-child(1) {
|
||||
&::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin-right: 10px;
|
||||
/* 关键:增加图例圆点和文字之间的间距 */
|
||||
margin-bottom: 2px;
|
||||
background-color: rgba(91, 230, 190, 1);
|
||||
background-color: rgba(98, 213, 180, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,107 +1,81 @@
|
||||
<template>
|
||||
<div class="coreBar" style="width: 100%;">
|
||||
<!-- 循环渲染item,data中配置的每一项对应一个卡片 -->
|
||||
<div class="barTop">
|
||||
<div class="title">生产指标趋势</div>
|
||||
|
||||
<div class="button-group">
|
||||
<!-- 按钮1:单价 -->
|
||||
<div class="item-button" :class="{ active: activeButton === 0 }" @click="activeButton = 0">
|
||||
总成本
|
||||
</div>
|
||||
<!-- 分割线0:单价右侧 -->
|
||||
<div class="item-button" :class="{ active: activeButton === 0 }" @click="activeButton = 0">总成本</div>
|
||||
<div class="button-line lineOne" v-if="activeButton !== 0 && activeButton !== 1"></div>
|
||||
|
||||
<!-- 按钮2:净价 -->
|
||||
<div class="item-button" :class="{ active: activeButton === 1 }" @click="activeButton = 1">
|
||||
原片成本
|
||||
</div>
|
||||
<!-- 分割线1:净价右侧 -->
|
||||
<div class="item-button" :class="{ active: activeButton === 1 }" @click="activeButton = 1">原片成本</div>
|
||||
<div class="button-line lineTwo" v-if="activeButton !== 1 && activeButton !== 2"></div>
|
||||
|
||||
<!-- 按钮3:销量 -->
|
||||
<div class="item-button" :class="{ active: activeButton === 2 }" @click="activeButton = 2">
|
||||
加工成本
|
||||
</div>
|
||||
<!-- 分割线2:销量右侧 -->
|
||||
<div class="item-button" :class="{ active: activeButton === 2 }" @click="activeButton = 2">加工成本</div>
|
||||
<div class="button-line lineThree" v-if="activeButton !== 2 && activeButton !== 3"></div>
|
||||
|
||||
<!-- 按钮4:双镀产品 -->
|
||||
<div class="item-button" style="width: 75px;" :class="{ active: activeButton === 3 }" @click="activeButton = 3">
|
||||
原片成品率
|
||||
</div>
|
||||
原片成品率</div>
|
||||
<div class="button-line lineFour" v-if="activeButton !== 3 && activeButton !== 4"></div>
|
||||
|
||||
<!-- 按钮5:投入产出率 -->
|
||||
<div class="item-button" style="width: 75px;" :class="{ active: activeButton === 4 }" @click="activeButton = 4">
|
||||
投入产出率
|
||||
</div>
|
||||
投入产出率</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lineBottom" style="height: 219px; width: 100%">
|
||||
<coreLineChart style="height: 219px; width: 500px" />
|
||||
<div class="lineBottom" style="height: 219px; width: 100%" v-if="isLineDataReady">
|
||||
<!-- 核心改动:动态传递数据给子组件 -->
|
||||
<coreLineChart style="height: 219px; width: 100%" :chart-data="selectedChartData" :dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import coreLineChart from './productBar.vue';
|
||||
|
||||
export default {
|
||||
name: "Container",
|
||||
components: { coreLineChart },
|
||||
props: ["name", "size", "icon"],
|
||||
props: ["lineData",'dateData'], // 接收父组件传递的完整line数据对象
|
||||
data() {
|
||||
return {
|
||||
// 所有item的数据配置,后续修改直接操作这个数组即可
|
||||
activeButton: 0, // 初始激活第一个按钮(索引0)
|
||||
itemList: [
|
||||
{
|
||||
unit: "单价·元/m²", // 标题
|
||||
targetValue: 16, // 左侧目标值
|
||||
currentValue: 14.5, // 右侧当前值(可根据实际需求修改)
|
||||
progress: 90, // 进度百分比
|
||||
},
|
||||
{
|
||||
unit: "净价·元/m²",
|
||||
targetValue: 16,
|
||||
currentValue: 15.2,
|
||||
progress: 85,
|
||||
},
|
||||
{
|
||||
unit: "销量·万m²",
|
||||
targetValue: 20,
|
||||
currentValue: 16,
|
||||
progress: 80,
|
||||
},
|
||||
{
|
||||
unit: "双镀面板·万m²",
|
||||
targetValue: 15,
|
||||
currentValue: 13.8,
|
||||
progress: 92,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
computed: {
|
||||
isLineDataReady() {
|
||||
// 确保 lineData 是一个对象,而不是 null 或 undefined
|
||||
return this.lineData && typeof this.lineData === 'object';
|
||||
},
|
||||
// 核心改动:计算属性,根据activeButton动态返回选中的数据
|
||||
selectedChartData() {
|
||||
// 定义按钮索引与lineData中key的映射关系
|
||||
const dataKeyMap = [
|
||||
'总成本',
|
||||
'原片成本',
|
||||
'加工成本',
|
||||
'原片成品率',
|
||||
'投入产出率'
|
||||
];
|
||||
|
||||
// 根据当前激活的按钮索引获取对应的数据key
|
||||
const selectedKey = dataKeyMap[this.activeButton];
|
||||
console.log(this.lineData[selectedKey]);
|
||||
|
||||
|
||||
// 从lineData中获取对应的数据,如果不存在则返回一个空对象以防止报错
|
||||
return this.lineData ? this.lineData[selectedKey] || {} : {};
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* (你的样式代码保持不变) */
|
||||
.coreBar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 12px;
|
||||
|
||||
// grid-template-columns: 1fr 1fr;
|
||||
// grid-template-rows: 1fr 1fr;
|
||||
// gap: 8px;
|
||||
.barTop {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
|
||||
.title {
|
||||
// width: 124px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
@@ -116,42 +90,40 @@ export default {
|
||||
.button-group {
|
||||
display: flex;
|
||||
position: relative;
|
||||
// justify-content: space-around;
|
||||
gap: 2px;
|
||||
width: 327px;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
background: #ecf4fe;
|
||||
border-radius: 12px;
|
||||
.button-line {
|
||||
position: absolute;
|
||||
// top: 5px;
|
||||
// left: 54px;
|
||||
width: 1px;
|
||||
height: 14px;
|
||||
border: 1px solid rgba(11, 88, 255, 0.25);
|
||||
}
|
||||
|
||||
.button-line {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 14px;
|
||||
border: 1px solid rgba(11, 88, 255, 0.25);
|
||||
}
|
||||
|
||||
.lineOne {
|
||||
top: 5px;
|
||||
left: 57px;
|
||||
}
|
||||
|
||||
.lineTwo {
|
||||
top: 5px;
|
||||
left: 118px;
|
||||
}
|
||||
|
||||
.lineThree {
|
||||
top: 5px;
|
||||
left: 177px;
|
||||
}
|
||||
.lineFour {
|
||||
top: 5px;
|
||||
left: 268px;
|
||||
}
|
||||
|
||||
// .button-line:nth-child(3) {
|
||||
// top: 5px;
|
||||
// left: 216px;
|
||||
// }
|
||||
.lineFour {
|
||||
top: 5px;
|
||||
left: 268px;
|
||||
}
|
||||
|
||||
.item-button {
|
||||
cursor: pointer;
|
||||
width: 59px;
|
||||
@@ -164,21 +136,14 @@ export default {
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
}
|
||||
// .item-button:nth-child(6){
|
||||
// width: 75px;
|
||||
|
||||
// }
|
||||
// .item-button:nth-child(8) {
|
||||
// width: 75px;
|
||||
|
||||
// }
|
||||
.item-button.active {
|
||||
width: 59px;
|
||||
height: 24px;
|
||||
background: #3071ff;
|
||||
border-radius: 12px;
|
||||
color: #ffffff; // 文字变白,提高对比度
|
||||
font-weight: 500; // 文字加粗,突出激活状态
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,8 +118,20 @@ export default {
|
||||
yAxisIndex: 0,
|
||||
barWidth: 18,
|
||||
itemStyle: {
|
||||
color: '#2889FF',
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
// 移除多余的 normal 层级,直接配置 color 渐变
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
|
||||
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
|
||||
]
|
||||
},
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
borderWidth: 0
|
||||
},
|
||||
data: targetData.map(item => item.targetValue)
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container name="利润数据总览" icon="cockpitItemIcon" size="profitTopBasic" topSize="middle">
|
||||
<Container name="利润数据总览·万元" icon="cockpitItemIcon" size="profitTopBasic" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
|
||||
@@ -151,9 +151,8 @@ export default {
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
min: 0,
|
||||
max: (value) => value.max > 0 ? Math.ceil(value.max * 1.1) : 10,
|
||||
scale: false,
|
||||
scale: true,
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
@@ -162,7 +161,6 @@ export default {
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
@@ -171,8 +169,8 @@ export default {
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
min: 0,
|
||||
max: 100,
|
||||
// min: 0,
|
||||
// max: 100,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="coreItem">
|
||||
<div class="item" @click="handleRoute(item.route)" v-for="(item, index) in itemList" :key="index">
|
||||
<div class="unit">{{ item.unit }}</div>
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="item-content">
|
||||
<div class="content-wrapper">
|
||||
<div class="left">
|
||||
@@ -54,45 +54,83 @@
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
props: ["name", "size", "icon"],
|
||||
props: ["finance",'dateData'],
|
||||
data() {
|
||||
return {
|
||||
itemList: [
|
||||
{
|
||||
unit: "营业收入·万元",
|
||||
targetValue: 16,
|
||||
currentValue: 17.2, // 大于目标值(绿色)
|
||||
progress: 107.5,
|
||||
route: 'operatingRevenue'
|
||||
},
|
||||
{
|
||||
unit: "经营性利润·万元",
|
||||
targetValue: 16,
|
||||
currentValue: 16, // 等于目标值(绿色)
|
||||
progress: 100,
|
||||
route: 'profitAnalysis'
|
||||
},
|
||||
{
|
||||
unit: "利润总额·万元",
|
||||
targetValue: 16,
|
||||
currentValue: 14.8, // 小于目标值(黄色)
|
||||
progress: 92.5,
|
||||
route: 'profitAnalysis'
|
||||
},
|
||||
{
|
||||
unit: "毛利率·%",
|
||||
targetValue: 16,
|
||||
currentValue: 15.5, // 小于目标值(黄色)
|
||||
progress: 96.875,
|
||||
route: 'profitAnalysis'
|
||||
}
|
||||
]
|
||||
// itemList: [
|
||||
// {
|
||||
// name: "营业收入·万元",
|
||||
// targetValue: 16,
|
||||
// currentValue: 17.2, // 大于目标值(绿色)
|
||||
// progress: 107.5,
|
||||
// route: 'operatingRevenue'
|
||||
// },
|
||||
// {
|
||||
// name: "经营性利润·万元",
|
||||
// targetValue: 16,
|
||||
// currentValue: 16, // 等于目标值(绿色)
|
||||
// progress: 100,
|
||||
// route: 'profitAnalysis'
|
||||
// },
|
||||
// {
|
||||
// name: "利润总额·万元",
|
||||
// targetValue: 16,
|
||||
// currentValue: 14.8, // 小于目标值(黄色)
|
||||
// progress: 92.5,
|
||||
// route: 'profitAnalysis'
|
||||
// },
|
||||
// {
|
||||
// name: "毛利率·%",
|
||||
// targetValue: 16,
|
||||
// currentValue: 15.5, // 小于目标值(黄色)
|
||||
// progress: 96.875,
|
||||
// route: 'profitAnalysis'
|
||||
// }
|
||||
// ]
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
finance: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.itemList = this.transformData(newVal);
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
transformData(rawData) {
|
||||
// 定义指标映射关系,包括名称、对应的数据键和路由
|
||||
const Mapping = [
|
||||
{ key: 'operatingRevenue', name: '营业收入·万元', route: 'operatingRevenue' },
|
||||
{ key: 'operatingIncome', name: '经营性利润·万元', route: 'profitAnalysis' },
|
||||
{ key: 'totalProfit', name: '利润总额·万元', route: 'profitAnalysis' },
|
||||
{ key: 'grossMargin', name: '毛利率·%', route: 'profitAnalysis' }
|
||||
];
|
||||
|
||||
// 遍历映射关系,转换数据
|
||||
return Mapping.map(mappingItem => {
|
||||
const data = rawData[mappingItem.key] || { rate: 0, real: 0, target: 0 };
|
||||
|
||||
return {
|
||||
name: mappingItem.name,
|
||||
targetValue: data.target,
|
||||
currentValue: data.real,
|
||||
progress: Math.round(data.rate), // 将小数率转换为百分比并四舍五入
|
||||
route: mappingItem.route
|
||||
};
|
||||
});
|
||||
},
|
||||
handleRoute(route) {
|
||||
if (route) {
|
||||
this.$router.push({ path: route });
|
||||
this.$router.push({
|
||||
path: route,
|
||||
query: {
|
||||
dateData: this.dateData
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,7 +158,7 @@ export default {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.unit {
|
||||
.name {
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container name="产销数据总览" icon="cockpitItemIcon" size="profitTopBasic" topSize="middle">
|
||||
<Container name="产销数据总览·万㎡" icon="cockpitItemIcon" size="profitTopBasic" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
|
||||
@@ -47,46 +47,63 @@
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
props: ["name", "size", "icon"],
|
||||
props: ["sale", "dateData"],
|
||||
data() {
|
||||
return {
|
||||
itemList: [
|
||||
{
|
||||
unit: "单价·元/㎡",
|
||||
targetValue: 16,
|
||||
currentValue: 14.5, // 小于目标值(黄色)
|
||||
progress: 90,
|
||||
path: "/cost/cost"
|
||||
},
|
||||
{
|
||||
unit: "净价·元/㎡",
|
||||
targetValue: 16,
|
||||
currentValue: 16, // 等于目标值(默认灰色)
|
||||
progress: 100,
|
||||
path: "/cost/cost"
|
||||
},
|
||||
{
|
||||
unit: "销量·万㎡",
|
||||
targetValue: 20,
|
||||
currentValue: 22, // 大于目标值(绿色)
|
||||
progress: 110,
|
||||
path: "PSIAnal"
|
||||
},
|
||||
{
|
||||
unit: "双镀面板·万㎡",
|
||||
targetValue: 15,
|
||||
currentValue: 13.8, // 小于目标值(黄色)
|
||||
progress: 92,
|
||||
path: "PSIAnal"
|
||||
}
|
||||
]
|
||||
itemList: [] // 初始化为空数组,等待数据加载
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听 sale 数据变化,实时更新 itemList
|
||||
sale: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.itemList = this.transformData(newVal);
|
||||
}
|
||||
},
|
||||
immediate: true, // 组件初始化时立即执行一次
|
||||
deep: true // 深度监听对象内部属性变化
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 核心转换函数:将 sale 对象转换为 itemList 数组
|
||||
* @param {Object} rawData - 原始的 sale 数据对象
|
||||
* @returns {Array} - 转换后的 itemList 数组
|
||||
*/
|
||||
transformData(rawData) {
|
||||
// 定义销售指标映射关系(键名、显示名称、单位、路由路径)
|
||||
const saleMapping = [
|
||||
{ key: 'unitPrice', unit: '单价·元/㎡', path: '/cost/cost' },
|
||||
{ key: 'netPrice', unit: '净价·元/㎡', path: '/cost/cost' },
|
||||
{ key: 'sales', unit: '销量·万㎡', path: 'PSIAnal' },
|
||||
{ key: 'panel', unit: '双镀面板·万㎡', path: 'PSIAnal' }
|
||||
];
|
||||
|
||||
// 遍历映射关系,转换数据
|
||||
return saleMapping.map(mappingItem => {
|
||||
// 获取对应指标的数据,若不存在则使用默认值
|
||||
const indicatorData = rawData[mappingItem.key] || { rate: 0, real: 0, target: 0 };
|
||||
|
||||
return {
|
||||
unit: mappingItem.unit,
|
||||
targetValue: indicatorData.target, // 目标值
|
||||
currentValue: indicatorData.real, // 实际值
|
||||
progress: Math.round(indicatorData.rate * 100), // 完成率(百分比,四舍五入)
|
||||
path: mappingItem.path // 路由路径
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
// 处理路由跳转
|
||||
handleRouter(obj) {
|
||||
if (obj.path) {
|
||||
this.$router.push({ path: obj.path });
|
||||
this.$router.push({
|
||||
path: obj.path,
|
||||
query: {
|
||||
dateData:this.dateData
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,69 +40,114 @@
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
props: ["name", "size", "icon"],
|
||||
// 接收父组件传递过来的原始 itemList 对象
|
||||
props: ['rawItemList','dateData'],
|
||||
data() {
|
||||
return {
|
||||
itemList: [
|
||||
{
|
||||
unit: "总成本·元/㎡",
|
||||
route: "cost/cost",
|
||||
target: 16,
|
||||
actual: 16,
|
||||
progress: 100
|
||||
},
|
||||
{
|
||||
unit: "原片成本·元/㎡",
|
||||
route: "cost/cost",
|
||||
target: 16,
|
||||
actual: 16,
|
||||
progress: 110
|
||||
},
|
||||
{
|
||||
unit: "加工成本·元/㎡",
|
||||
route: "cost/cost",
|
||||
target: 16,
|
||||
actual: 16,
|
||||
progress: 85
|
||||
},
|
||||
{
|
||||
unit: "原片成品率·%",
|
||||
target: 95,
|
||||
actual: 92,
|
||||
progress: 97
|
||||
},
|
||||
{
|
||||
unit: "投入产出率·%",
|
||||
target: 88,
|
||||
actual: 90,
|
||||
progress: 102
|
||||
}
|
||||
],
|
||||
// 组件内部用于渲染的数组
|
||||
itemList: [],
|
||||
activeIndex: -1
|
||||
};
|
||||
},
|
||||
// 监听 props 中的 rawItemList 变化
|
||||
watch: {
|
||||
rawItemList: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.itemList = this.transformData(newVal);
|
||||
}
|
||||
},
|
||||
immediate: true, // 组件初始化时立即执行一次
|
||||
deep: true // 深度监听对象内部变化
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 核心转换函数:将对象转换为组件需要的数组格式
|
||||
* @param {Object} rawData - 父组件传递的原始数据对象
|
||||
* @returns {Array} - 转换后的数组
|
||||
*/
|
||||
transformData(rawData) {
|
||||
// 定义一个映射关系,将后端字段名与前端显示信息关联起来
|
||||
const dataMap = [
|
||||
{
|
||||
key: 'totalCost',
|
||||
unit: '总成本·元/㎡',
|
||||
route: 'cost/cost'
|
||||
},
|
||||
{
|
||||
key: 'rawCost',
|
||||
unit: '原片成本·元/㎡',
|
||||
route: 'cost/cost'
|
||||
},
|
||||
{
|
||||
key: 'processCost',
|
||||
unit: '加工成本·元/㎡',
|
||||
route: 'cost/cost'
|
||||
},
|
||||
{
|
||||
key: 'rawYield',
|
||||
unit: '原片成品率·%',
|
||||
route: '' // 假设这个没有路由
|
||||
},
|
||||
{
|
||||
key: 'ioYield',
|
||||
unit: '投入产出率·%',
|
||||
route: '' // 假设这个没有路由
|
||||
}
|
||||
];
|
||||
|
||||
// 使用 map 方法将 dataMap 数组转换为组件需要的 itemList 数组
|
||||
return dataMap.map(itemInfo => {
|
||||
const rawItem = rawData[itemInfo.key] || {};
|
||||
// 计算进度百分比,确保不小于0
|
||||
const progress = Math.max(0, Math.round((rawItem.rate || 0)));
|
||||
|
||||
return {
|
||||
unit: itemInfo.unit,
|
||||
route: itemInfo.route,
|
||||
target: rawItem.target || 0,
|
||||
actual: rawItem.real || 0,
|
||||
progress: progress
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
handleItemClick(index) {
|
||||
const currentItem = this.itemList[index];
|
||||
console.log(`点击了第${index + 1}个item:`, currentItem.unit);
|
||||
this.$emit('item-click', { index, ...currentItem });
|
||||
this.activeIndex = index;
|
||||
if (currentItem.route) {
|
||||
this.$router.push({ path: currentItem.route });
|
||||
this.$router.push({
|
||||
path: currentItem.route,
|
||||
query: {
|
||||
dateData: this.dateData
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 判断颜色的方法
|
||||
getColor(index) {
|
||||
const { actual, target } = this.itemList[index];
|
||||
return actual >= target
|
||||
? "rgba(98, 213, 180, 1)"
|
||||
: "rgba(249, 164, 74, 1)";
|
||||
const { actual, target, progress } = this.itemList[index];
|
||||
|
||||
// 新增条件:如果实际值、目标值和进度都为0,则显示绿色
|
||||
if (actual === 0 && target === 0 && progress === 0) {
|
||||
return "rgba(98, 213, 180, 1)"; // 绿色
|
||||
}
|
||||
|
||||
// 原有的通用判断逻辑
|
||||
return progress >= 100
|
||||
? "rgba(98, 213, 180, 1)" // 绿色
|
||||
: "rgba(249, 164, 74, 1)"; // 橙色
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* (你的样式代码保持不变) */
|
||||
.coreItem {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -162,9 +162,9 @@ export default {
|
||||
getData(obj) {
|
||||
// obj.levelId = 1
|
||||
getCostAnalysisList({
|
||||
startTime: 1762704000000,
|
||||
endTime: 1762790400000,
|
||||
mode:1
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}).then((res) => {
|
||||
this.costOverviews = res.data.costOverviews
|
||||
this.piecesCostViews = res.data.piecesCostViews
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
<div class="visual-base-table-container">
|
||||
<el-table :max-height="maxHeight" ref="scroll_Table" @mouseenter.native="autoScroll(true)"
|
||||
@mouseleave.native="autoScroll(false)" v-loading="isLoading"
|
||||
:header-cell-style="{ background: 'rgba(218, 226, 237, 1)', color: 'rgba(0, 0, 0, .6)',padding:'3px 2px'}" :row-style="setRowStyle"
|
||||
:data="renderData" border style="width: 100%; background: transparent">
|
||||
:header-cell-style="{ background: 'rgba(218, 226, 237, 1)', color: 'rgba(0, 0, 0, .6)', padding: '3px 2px' }"
|
||||
:row-style="setRowStyle" :data="renderData" border style="width: 100%; background: transparent">
|
||||
<el-table-column v-if="page && limit && showIndex" prop="_pageIndex" label="序号" :width="70" align="center" />
|
||||
<el-table-column v-for="item in renderTableHeadList" :key="item.prop" :show-overflow-tooltip="showOverflow"
|
||||
v-bind="item">
|
||||
<template slot-scope="scope">
|
||||
|
||||
<component :is="item.subcomponent" v-if="item.subcomponent" :inject-data="{...scope.row, ...item}"
|
||||
<component :is="item.subcomponent" v-if="item.subcomponent" :inject-data="{ ...scope.row, ...item }"
|
||||
@emitData="emitData" />
|
||||
<span v-else>{{ scope.row[item.prop] | commonFilter(item.filter) }}</span>
|
||||
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
maxHeight: {
|
||||
type: [Number, String], // 支持数字(如300)或字符串(如'300px')
|
||||
required: false,
|
||||
default: 230 // 原固定值,作为默认 fallback
|
||||
default: 200 // 原固定值,作为默认 fallback
|
||||
},
|
||||
tableData: {
|
||||
type: Array,
|
||||
@@ -130,7 +130,7 @@ export default {
|
||||
if (divData.scrollTop + divData.clientHeight >= divData.scrollHeight - 1) {
|
||||
// 滚动到底部后,重置到顶部(延迟一点更自然)
|
||||
// setTimeout(() => {
|
||||
divData.scrollTop = 0
|
||||
divData.scrollTop = 0
|
||||
// }, 2000); // 停顿500ms后再从头滚动
|
||||
}
|
||||
}, 200) // 滚动速度(数值越小越快)
|
||||
@@ -144,19 +144,19 @@ export default {
|
||||
return {
|
||||
background: '#F9FCFF',
|
||||
color: 'rgba(87, 87, 87, 1)',
|
||||
height: 35 + 'px',
|
||||
height: 35 + 'px',
|
||||
lineHeight: 26 + 'px',
|
||||
padding: 0,
|
||||
fontSize: 12 + 'px'
|
||||
fontSize: 12 + 'px'
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
background: 'rgba(239, 243, 248, 1)',
|
||||
color: 'rgba(87, 87, 87, 1)',
|
||||
height: 35 + 'px',
|
||||
lineHeight: 26 + 'px',
|
||||
height: 35 + 'px',
|
||||
lineHeight: 26 + 'px',
|
||||
padding: 0,
|
||||
fontSize: 12 + 'px'
|
||||
fontSize: 12 + 'px'
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -171,19 +171,25 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
// @import "./styles/index.scss";
|
||||
.visual-base-table-container {
|
||||
.el-table {
|
||||
.el-table {
|
||||
border: 0;
|
||||
|
||||
// .el-table__body-wrapper::-webkit-scrollbar-thumb {
|
||||
// background-color: blue;
|
||||
// border-radius: 3px;
|
||||
// }
|
||||
// 关键修改:隐藏滚动条但保留滚动功能
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background: transparent;
|
||||
}
|
||||
// 隐藏表头的gutter
|
||||
.el-table__header .el-table__cell.gutter {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// 隐藏表头的gutter
|
||||
.el-table__header .el-table__cell.gutter {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// 表格主体内容区滚动条处理
|
||||
.el-table__body-wrapper {
|
||||
&::-webkit-scrollbar {
|
||||
@@ -217,24 +223,31 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-table::before,.el-table--border::after {
|
||||
|
||||
.el-table::before,
|
||||
.el-table--border::after {
|
||||
background-color: transparent;
|
||||
}
|
||||
.el-table th,td{
|
||||
|
||||
.el-table th,
|
||||
td {
|
||||
border-color: rgba(221, 221, 221, 1) !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.el-table tr {
|
||||
background: transparent;
|
||||
}
|
||||
.el-table__row:hover > td {
|
||||
background-color: rgba(79,114,136,0.29) !important;
|
||||
}
|
||||
|
||||
.el-table__row--striped:hover > td {
|
||||
background-color: rgba(79,114,136,0.29) !important;
|
||||
.el-table__row:hover>td {
|
||||
background-color: rgba(79, 114, 136, 0.29) !important;
|
||||
}
|
||||
|
||||
.el-table__row--striped:hover>td {
|
||||
background-color: rgba(79, 114, 136, 0.29) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// .setting {
|
||||
// text-align: right;
|
||||
// padding: 15px;
|
||||
@@ -246,5 +259,4 @@ export default {
|
||||
// @extend .pointer;
|
||||
// }
|
||||
// }
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,33 +1,24 @@
|
||||
<template>
|
||||
<div class="coreBar">
|
||||
<div class="barTop">
|
||||
<!-- 标题:单独左对齐 -->
|
||||
<!-- <div class="title">销售指标趋势</div> -->
|
||||
<!-- 关键:新增右侧容器,包裹图例和按钮组,实现整体靠右 -->
|
||||
<div class="right-container">
|
||||
<div class="legend">
|
||||
<span class="legend-item">
|
||||
<span class="legend-icon square target"></span>
|
||||
目标
|
||||
</span>
|
||||
<!-- 给第三个、第四个图例项加 close-item 类 -->
|
||||
<span class="legend-item">
|
||||
<span class="legend-icon square achieved"></span>
|
||||
实际
|
||||
</span>
|
||||
<!-- <span class="legend-item close-item">
|
||||
<span class="legend-icon square unachieved"></span>
|
||||
|
||||
</span> -->
|
||||
</div>
|
||||
<!-- 按钮组:改为下拉框样式,仅宽度与第二个组件一致 -->
|
||||
<div class="button-group">
|
||||
<div class="item-button category-btn">
|
||||
<span class="item-text" style="width: 88px;">类目选择</span>
|
||||
</div>
|
||||
<div class="dropdown-container">
|
||||
<div class="item-button profit-btn active" @click.stop="isDropdownShow = !isDropdownShow">
|
||||
<span class="item-text profit-text">{{ selectedProfit || '原料' }}</span>
|
||||
<span class="item-text profit-text">{{ selectedProfit || '请选择' }}</span>
|
||||
<span class="dropdown-arrow" :class="{ 'rotate': isDropdownShow }"></span>
|
||||
</div>
|
||||
<div class="dropdown-options" v-if="isDropdownShow">
|
||||
@@ -41,7 +32,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="lineBottom" style="height: 100%; width: 1590px">
|
||||
<costBaseBarChart :yName="yName" style="height: 99%; width: 1590px" />
|
||||
<!-- 传递筛选后的数据给图表组件 -->
|
||||
<costBaseBarChart :yName="yName" :chartData="filteredData" style="height: 99%; width: 1590px" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -51,15 +43,40 @@ import costBaseBarChart from './costBaseBarChart.vue';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: { costBaseBarChart },
|
||||
props: ['dateData','yName'],
|
||||
props: ['dateData', 'yName', 'categoryData'],
|
||||
data() {
|
||||
return {
|
||||
isDropdownShow: false,
|
||||
selectedProfit: '原料', // 默认选中"原料"
|
||||
profitOptions: ['原料', '其他选项1', '其他选项2'], // 可根据实际需求修改选项
|
||||
selectedProfit: null, // 选中的名称,初始为null
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
computed: {
|
||||
// 从categoryData中提取name作为下拉选项
|
||||
profitOptions() {
|
||||
return this.categoryData.map(item => item.name) || [];
|
||||
},
|
||||
// 根据选中的名称筛选数据
|
||||
filteredData() {
|
||||
if (!this.selectedProfit || !this.categoryData.length) {
|
||||
// 未选择时默认取第一个分类的数据(或空)
|
||||
return this.categoryData[0] || {};
|
||||
}
|
||||
// 找到选中名称对应的分类数据
|
||||
return this.categoryData.find(item => item.name === this.selectedProfit) || {};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
categoryData: {
|
||||
handler() {
|
||||
// 当分类数据变化时,若没有选中项则默认选中第一个
|
||||
if (this.categoryData.length && !this.selectedProfit) {
|
||||
this.selectedProfit = this.categoryData[0].name;
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectProfit(item) {
|
||||
this.selectedProfit = item;
|
||||
@@ -82,6 +99,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 原有样式保持不变 */
|
||||
.coreBar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -95,19 +113,6 @@ export default {
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
|
||||
.title {
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.right-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -138,32 +143,11 @@ export default {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.legend-icon.line {
|
||||
width: 12px;
|
||||
height: 2px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
top: -2px;
|
||||
left: 3px;
|
||||
width: 6px;
|
||||
border-radius: 50%;
|
||||
height: 6px;
|
||||
background-color: rgba(40, 138, 255, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.legend-icon.square {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.yield {
|
||||
background: rgba(40, 138, 255, 1);
|
||||
}
|
||||
|
||||
.target {
|
||||
background: #2889FF;
|
||||
}
|
||||
@@ -172,15 +156,6 @@ export default {
|
||||
background: rgba(40, 203, 151, 1);
|
||||
}
|
||||
|
||||
.unachieved {
|
||||
background: rgba(255, 132, 0, 1);
|
||||
}
|
||||
|
||||
.legend-item.close-item+.legend-item.close-item {
|
||||
margin-left: -8px;
|
||||
}
|
||||
|
||||
// 按钮组:完全复用第二个组件样式,仅保持原有宽度适配
|
||||
.button-group {
|
||||
display: flex;
|
||||
position: relative;
|
||||
@@ -222,7 +197,7 @@ export default {
|
||||
}
|
||||
|
||||
.profit-btn {
|
||||
width: 102px; // 保持原组件要求的宽度,其余样式与第二个组件一致
|
||||
width: 102px;
|
||||
border-top-right-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
position: relative;
|
||||
@@ -264,7 +239,7 @@ export default {
|
||||
top: 100%;
|
||||
right: 0;
|
||||
margin-top: 2px;
|
||||
width: 102px; // 与按钮宽度一致
|
||||
width: 102px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
@@ -8,27 +8,68 @@ import * as echarts from 'echarts';
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {};
|
||||
return {
|
||||
myChart: null // 保存图表实例,方便更新
|
||||
};
|
||||
},
|
||||
props: {
|
||||
yName: {
|
||||
type: String,
|
||||
default: () => '元/㎡'
|
||||
},
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
this.updateChart(); // 数据变化时更新图表
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initData();
|
||||
this.initChart();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
console.error('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
const myChart = echarts.init(chartDom);
|
||||
this.myChart = echarts.init(chartDom);
|
||||
this.updateChart(); // 初始化图表数据
|
||||
|
||||
// 窗口缩放适配
|
||||
window.addEventListener('resize', () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
});
|
||||
|
||||
// 组件销毁清理
|
||||
this.$once('hook:destroyed', () => {
|
||||
window.removeEventListener('resize', () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
});
|
||||
this.myChart && this.myChart.dispose();
|
||||
});
|
||||
},
|
||||
|
||||
updateChart() {
|
||||
if (!this.myChart || !this.chartData.rawData || !this.chartData.timeArray) {
|
||||
return; // 图表未初始化或数据不完整时不更新
|
||||
}
|
||||
|
||||
// 从rawData中提取目标和实际数据
|
||||
const targetData = this.chartData.rawData.map(item => item.target); // 目标数据数组
|
||||
const actualData = this.chartData.rawData.map(item => item.value); // 实际数据数组
|
||||
const xAxisData = this.chartData.timeArray; // 横坐标时间数组
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
@@ -38,26 +79,24 @@ export default {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
},
|
||||
// 优化tooltip内容,区分各系列含义
|
||||
formatter: (params) => {
|
||||
let html = `${params[0].axisValue}<br/>`;
|
||||
params.forEach(item => {
|
||||
// 直接使用系列名,无需映射,仅保留单位判断
|
||||
html += `${item.marker} ${item.seriesName}: ${item.value}${item.seriesName === '完成率' ? '%' : '片'}<br/>`;
|
||||
html += `${item.marker} ${item.seriesName}: ${item.value}${this.yName}<br/>`;
|
||||
});
|
||||
return html;
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 30, // 增大底部间距,避免柱子与X轴标签重叠
|
||||
bottom: 30,
|
||||
right: 70,
|
||||
left: 40,
|
||||
left: 60,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true, // 开启边界间隙,让柱子居中显示,不贴边
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
@@ -67,13 +106,12 @@ export default {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0] // 标签向下偏移,避免与柱子底部重叠
|
||||
padding: [5, 0, 0, 0]
|
||||
},
|
||||
data: ['6月', '7月', '8月', '9月', '10月', '11月']
|
||||
data: xAxisData // 使用timeArray作为横坐标
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
// 左侧Y轴:目标/达标/未达标(数量,单位“片”)
|
||||
{
|
||||
type: 'value',
|
||||
name: this.yName,
|
||||
@@ -82,77 +120,28 @@ export default {
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
min: 0, // 最小值设0,确保柱子从X轴底部开始,不超过X轴
|
||||
max: (value) => Math.ceil(value.max * 1.1), // 最大值留10%余量,避免柱子顶满
|
||||
scale: false, // 关闭缩放,强制从0开始
|
||||
// min: 0,
|
||||
// max: (value) => value.max > 0 ? Math.ceil(value.max * 1.1) : 100, // 留10%余量
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
formatter: `{value}`
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
},
|
||||
// 右侧Y轴:完成率(百分比)
|
||||
// {
|
||||
// type: 'value',
|
||||
// // name: '%',
|
||||
// nameTextStyle: {
|
||||
// color: 'rgba(0, 0, 0, 0.45)',
|
||||
// fontSize: 12,
|
||||
// align: 'left'
|
||||
// },
|
||||
// min: 0,
|
||||
// max: 100, // 完成率最大100%,符合业务逻辑
|
||||
// axisTick: { show: false },
|
||||
// axisLabel: {
|
||||
// color: 'rgba(0, 0, 0, 0.45)',
|
||||
// fontSize: 12,
|
||||
// formatter: '{value}%'
|
||||
// },
|
||||
// splitLine: { show: false }, // 不重复显示分割线
|
||||
// axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
// splitNumber: 4
|
||||
// }
|
||||
splitNumber: 3
|
||||
}
|
||||
],
|
||||
series: [
|
||||
// 1. 完成率:折线图(绑定右侧百分比Y轴)
|
||||
// {
|
||||
// name: '产销率',
|
||||
// type: 'line',
|
||||
// yAxisIndex: 1, // 绑定右侧Y轴
|
||||
// lineStyle: {
|
||||
// color: 'rgba(40, 138, 255, .5)',
|
||||
// width: 2 // 线条加粗,突出重点
|
||||
// },
|
||||
// itemStyle: {
|
||||
// color: 'rgba(40, 138, 255, 1)',
|
||||
// borderColor: 'rgba(40, 138, 255, 1)', // 数据点白色边框,增强辨识度
|
||||
// borderWidth: 2,
|
||||
// radius: 4 // 数据点圆角,更圆润
|
||||
// },
|
||||
// areaStyle: {
|
||||
// opacity: 0.2, // 降低面积透明度,不抢柱状图视觉焦点
|
||||
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
// { offset: 0, color: 'rgba(40, 138, 255, .9)' },
|
||||
// { offset: 1, color: 'rgba(255, 132, 0, 0)' }
|
||||
// ])
|
||||
// },
|
||||
// data: [65, 78, 52, 85, 60, 95, 72], // 完成率数据(0-100)
|
||||
// symbol: 'circle', // 数据点为圆形
|
||||
// symbolSize: 6 // 数据点大小
|
||||
// },
|
||||
// 2. 目标:柱状图(绑定左侧数量Y轴)
|
||||
// 目标数据柱状图
|
||||
{
|
||||
name: '销量',
|
||||
name: '目标',
|
||||
type: 'bar',
|
||||
yAxisIndex: 0,
|
||||
barWidth: 24,
|
||||
// 关键修复:label 直接放在 series 下,而非 itemStyle 内
|
||||
itemStyle: {
|
||||
// 移除多余的 normal 层级,直接配置 color 渐变
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
@@ -167,17 +156,15 @@ export default {
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
borderWidth: 0
|
||||
},
|
||||
data: [200, 280, 180, 300, 220, 350]
|
||||
data: targetData // 目标数据数组
|
||||
},
|
||||
// 3. 达标:柱状图(绑定左侧数量Y轴)
|
||||
// 实际数据柱状图
|
||||
{
|
||||
name: '产量',
|
||||
name: '实际',
|
||||
type: 'bar',
|
||||
yAxisIndex: 0,
|
||||
barWidth: 24,
|
||||
// 关键修复:label 直接放在 series 下,而非 itemStyle 内
|
||||
itemStyle: {
|
||||
// 移除多余的 normal 层级,直接配置 color 渐变
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
@@ -192,50 +179,12 @@ export default {
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
borderWidth: 0
|
||||
},
|
||||
data: [130, 220, 95, 255, 132, 332] // 达标数据(小于目标)
|
||||
},
|
||||
// 4. 未达标:柱状图(绑定左侧数量Y轴)
|
||||
// {
|
||||
// name: '未达标',
|
||||
// type: 'bar',
|
||||
// yAxisIndex: 0,
|
||||
// barWidth: 18,
|
||||
// itemStyle: {
|
||||
// color: 'rgba(249, 164, 74, 1)',
|
||||
// borderRadius: [4, 4, 0, 0],
|
||||
// borderWidth: 0
|
||||
// },
|
||||
// data: [70, 60, 85, 45, 88, 18, 78] // 未达标数据(目标-达标)
|
||||
// }
|
||||
],
|
||||
// 图例:区分各系列,点击可控制显示隐藏
|
||||
// legend: {
|
||||
// top: 0,
|
||||
// left: 'center',
|
||||
// itemWidth: 12,
|
||||
// itemHeight: 8,
|
||||
// textStyle: {
|
||||
// color: 'rgba(0, 0, 0, 0.45)',
|
||||
// fontSize: 12
|
||||
// },
|
||||
// data: ['完成率', '目标', '达标', '未达标']
|
||||
// }
|
||||
data: actualData // 实际数据数组
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
// 窗口缩放适配
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
|
||||
// 组件销毁清理
|
||||
this.$once('hook:destroyed', () => {
|
||||
window.removeEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
myChart.dispose();
|
||||
});
|
||||
this.myChart.setOption(option);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -260,7 +260,7 @@ export default {
|
||||
.middle-line {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(to right, rgba(40, 203, 151, 0.3), rgba(40, 203, 151, 0.8), rgba(40, 203, 151, 0.3));
|
||||
background: linear-gradient(to right, #cbcbcb rgba(255, 255, 255, 0));
|
||||
}
|
||||
|
||||
.number {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div style="width: 100%;">
|
||||
<Container name="领用明细" icon="cockpitItemIcon" size="" topSize="middle">
|
||||
<Container name="领用明细" icon="cockpitItemIcon" size="productMiddleBg" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style=" padding: 14px 16px; display: flex;width: 100%;">
|
||||
<div class="bottom"
|
||||
style="padding: 14px 16px; height: 620px; display: flex; width: 100%;;">
|
||||
style="padding: 14px 16px; height: 620px; display: flex; width: 100%;background-color: #ffffff;">
|
||||
<!-- <top-item /> -->
|
||||
<base-table style="width: 100%;" :page="1" :limit="10" :show-index="true" :beilv="1" :tableConfig="tableProps"
|
||||
:table-data="tableData" />
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div style="width: 100%;">
|
||||
<Container name="入账明细" icon="cockpitItemIcon" size="" topSize="middle">
|
||||
<Container name="入账明细" icon="cockpitItemIcon" size="productMiddleBg" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;width: 100%;">
|
||||
<div class="bottom"
|
||||
style="padding: 14px 16px; height: 620px; display: flex; width: 100%;">
|
||||
style="padding: 14px 16px; height: 620px; display: flex; width: 100%;background-color: #ffffff;">
|
||||
<!-- <top-item /> -->
|
||||
<base-table style="width: 100%;" :page="1" :limit="10" :show-index="true" :beilv="1" :tableConfig="tableProps"
|
||||
:table-data="tableData" />
|
||||
@@ -81,7 +81,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
<!-- <style lang='scss' scoped>
|
||||
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
|
||||
.scroll-container {
|
||||
/* 1. 固定容器高度:根据页面布局调整(示例300px,超出则滚动) */
|
||||
@@ -249,4 +249,4 @@ export default {
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
</style> -->
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div class="coreBar">
|
||||
<div class="lineBottom" style="height: 100%; width: 1590px">
|
||||
<profitImpactLineChart :yName="yName" style="height: 99%; width: 1590px" />
|
||||
<profitImpactLineChart :name="name" :seriesData="seriesData" :xData="xData" :yName="yName"
|
||||
style="height: 99%; width: 1590px" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -11,12 +12,22 @@ import profitImpactLineChart from './profitImpactLineChart.vue';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: { profitImpactLineChart },
|
||||
props: ['dateData','yName'],
|
||||
props: {
|
||||
seriesData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
xData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: () => { }
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isDropdownShow: false,
|
||||
selectedProfit: '原料', // 默认选中"原料"
|
||||
profitOptions: ['原料', '其他选项1', '其他选项2'], // 可根据实际需求修改选项
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
|
||||
@@ -11,10 +11,34 @@ export default {
|
||||
return {};
|
||||
},
|
||||
props: {
|
||||
yName: {
|
||||
type: String,
|
||||
default: () => '元/㎡'
|
||||
seriesData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
xData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: () => { }
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 监听 xData 变化,触发图表更新
|
||||
xData: {
|
||||
handler() {
|
||||
this.$nextTick(() => this.initData());
|
||||
},
|
||||
deep: true // 深度监听数组内元素变化
|
||||
},
|
||||
// 监听 seriesData 变化,触发图表更新
|
||||
seriesData: {
|
||||
handler() {
|
||||
this.$nextTick(() => this.initData());
|
||||
},
|
||||
deep: true // 深度监听数组内元素变化
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
@@ -69,7 +93,7 @@ export default {
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0] // 标签向下偏移,避免与柱子底部重叠
|
||||
},
|
||||
data: ['6月', '7月', '8月', '9月', '10月', '11月']
|
||||
data: this.xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
@@ -98,7 +122,7 @@ export default {
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '利润',
|
||||
name: this.name,
|
||||
type: 'line',
|
||||
// yAxisIndex: 1,
|
||||
lineStyle: {
|
||||
@@ -118,7 +142,7 @@ export default {
|
||||
{ offset: 1, color: 'rgba(40, 138, 255, 0)' }
|
||||
])
|
||||
},
|
||||
data: [200, 280, 180, 300, 220, 350],
|
||||
data: this.seriesData,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6
|
||||
},
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div style="width: 100%;">
|
||||
<Container :name="name" icon="cockpitItemIcon" size="operatingLarge" topSize="large">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;width: 100%;">
|
||||
<div class="bottom" style="height: 420px; display: flex; width: 100%;background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- <top-item /> -->
|
||||
<costBar :yName="yName" :dateData="dateData" />
|
||||
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
||||
<div class="bottom"
|
||||
style="height: 420px; display: flex; width: 100%; background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- 传递处理后的分类数据给子组件 -->
|
||||
<costBar :yName="yName" :categoryData="categoryData" />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import costBar from './costBar.vue'
|
||||
@@ -19,89 +19,151 @@ import costBar from './costBar.vue'
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, costBar },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
trendData: { // 接收父组件传递的设备数据数组
|
||||
trendData: { // 假设trendData是需要分类的原始数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
default: () => []
|
||||
},
|
||||
dateData: { // 接收父组件传递的设备数据数组
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
default: () => { }
|
||||
},
|
||||
yName: { // 接收父组件传递的设备数据数组
|
||||
yName: {
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
default: ''
|
||||
},
|
||||
name: { // 接收父组件传递的设备数据数组
|
||||
name: {
|
||||
type: String,
|
||||
default: () => '趋势图' // 默认空数组,避免报错
|
||||
default: '趋势图'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
chart: null,
|
||||
categoryData: [] // 存储分类结果:每个项包含name、timeArray(时间数组)、rawData(原始数据)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
trendData: { // 监听原始数据变化,重新分类处理
|
||||
handler() {
|
||||
this.processAndFormatData();
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
deep: true,
|
||||
immediate: true // 初始加载时立即处理
|
||||
},
|
||||
dateData: { // 监听模式变化,重新格式化时间
|
||||
handler() {
|
||||
console.log('dateData', this.dateData);
|
||||
this.processAndFormatData();
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 核心处理:按name分类 → 时间排序 → 提取name和时间数组
|
||||
*/
|
||||
processAndFormatData() {
|
||||
if (!this.trendData.length) {
|
||||
this.categoryData = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 按name分组
|
||||
const grouped = {};
|
||||
this.trendData.forEach(item => {
|
||||
const name = item.name;
|
||||
if (!grouped[name]) {
|
||||
grouped[name] = [];
|
||||
}
|
||||
grouped[name].push(item);
|
||||
});
|
||||
|
||||
// 2. 对每个分组按时间从早到晚排序,并提取时间数组
|
||||
const result = [];
|
||||
Object.keys(grouped).forEach(name => {
|
||||
// 按时间戳升序排序(时间从早到晚)
|
||||
const sortedData = grouped[name].sort((a, b) => a.time - b.time);
|
||||
|
||||
// 3. 提取时间数组(并根据mode格式化)
|
||||
const timeArray = sortedData.map(item => {
|
||||
return this.formatTimeByMode(item.time, this.dateData.mode);
|
||||
});
|
||||
|
||||
// 4. 组装分类结果
|
||||
result.push({
|
||||
name: name, // 分类名称
|
||||
timeArray: timeArray, // 格式化后的时间数组(从早到晚)
|
||||
rawData: sortedData // 排序后的原始数据(保留所有字段)
|
||||
});
|
||||
});
|
||||
|
||||
this.categoryData = result;
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据mode格式化单个时间戳
|
||||
* @param {Number} timeStamp 时间戳
|
||||
* @param {Number} mode 1-日(时分秒) 2-月(月-日) 3-年(年)
|
||||
* @returns 格式化后的时间字符串
|
||||
*/
|
||||
formatTimeByMode(timeStamp, mode) {
|
||||
if (!timeStamp) return '';
|
||||
const date = new Date(timeStamp);
|
||||
|
||||
switch (mode) {
|
||||
case 1: // 日模式:时分秒 (HH:MM:SS)
|
||||
return `${this.padZero(date.getHours())}:${this.padZero(date.getMinutes())}:${this.padZero(date.getSeconds())}`;
|
||||
case 2: // 月模式:月-日 (MM-DD)
|
||||
return `${this.padZero(date.getMonth() + 1)}-${this.padZero(date.getDate())}`; // 月份+1是因为getMonth()返回0-11
|
||||
case 3: // 年模式:年 (YYYY)
|
||||
return `${date.getFullYear()}`;
|
||||
default:
|
||||
return timeStamp.toString();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 数字补零(确保两位数)
|
||||
*/
|
||||
padZero(num) {
|
||||
return num < 10 ? `0${num}` : num;
|
||||
},
|
||||
|
||||
updateChart() {
|
||||
// 图表更新逻辑可基于categoryData处理
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
|
||||
/* 原有样式保持不变 */
|
||||
.scroll-container {
|
||||
/* 1. 固定容器高度:根据页面布局调整(示例300px,超出则滚动) */
|
||||
max-height: 210px;
|
||||
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
|
||||
overflow-y: auto;
|
||||
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
|
||||
overflow-x: hidden;
|
||||
/* 4. 内边距:与标题栏和容器边缘对齐 */
|
||||
padding: 10px 0;
|
||||
|
||||
/* 5. 隐藏滚动条(兼容主流浏览器) */
|
||||
/* Chrome/Safari */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* IE/Edge */
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
/* 设备项样式优化:增加间距,避免拥挤 */
|
||||
.proBarInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 8px 27px;
|
||||
/* 调整内边距,优化排版 */
|
||||
margin-bottom: 10px;
|
||||
/* 设备项之间的垂直间距 */
|
||||
}
|
||||
|
||||
/* 原有样式保留,优化细节 */
|
||||
.proBarInfoEqInfo {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
/* 垂直居中,避免序号/文字错位 */
|
||||
}
|
||||
|
||||
.slot {
|
||||
@@ -114,14 +176,12 @@ export default {
|
||||
font-size: 16px;
|
||||
color: #68B5FF;
|
||||
line-height: 23px;
|
||||
/* 垂直居中文字 */
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.eq-name {
|
||||
margin-left: 8px;
|
||||
/* 增加与序号的间距 */
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
@@ -147,7 +207,6 @@ export default {
|
||||
height: 14px;
|
||||
border: 1px solid #ADADAD;
|
||||
margin: 0 8px;
|
||||
/* 优化分割线间距 */
|
||||
}
|
||||
|
||||
.yield {
|
||||
@@ -164,22 +223,18 @@ export default {
|
||||
.proBarInfoEqInfoLeft {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* 序号和设备名垂直居中 */
|
||||
}
|
||||
|
||||
.proBarInfoEqInfoRight {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* 状态/分割线/百分比垂直居中 */
|
||||
}
|
||||
|
||||
.proBarWrapper {
|
||||
position: relative;
|
||||
height: 10px;
|
||||
margin-top: 6px;
|
||||
/* 进度条与上方信息的间距 */
|
||||
border-radius: 5px;
|
||||
/* 进度条圆角,优化视觉 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -198,10 +253,8 @@ export default {
|
||||
background: linear-gradient(65deg, rgba(53, 223, 247, 0) 0%, rgba(54, 220, 246, 0.92) 92%, #36F6E5 100%, #37ACF5 100%);
|
||||
border-radius: 5px;
|
||||
transition: width 0.3s ease;
|
||||
/* 进度变化时添加过渡动画,更流畅 */
|
||||
}
|
||||
|
||||
/* 图表相关样式保留 */
|
||||
.chartImgBottom {
|
||||
position: absolute;
|
||||
bottom: 45px;
|
||||
@@ -220,7 +273,6 @@ export default {
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;width: 100%;">
|
||||
<div class="bottom" style="height: 420px; display: flex; width: 100%;background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- <top-item /> -->
|
||||
<costBar :yName="yName" :dateData="dateData" />
|
||||
<costBar :yName="yName" :seriesData="seriesData" :xData="xData" :name="name" :dateData="dateData" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,23 +40,60 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
formattedList: [], // 格式化后的完整数据(含处理后的time)
|
||||
seriesData: [], // value数组
|
||||
xData: [] // 新增:存储格式化后的时间数组
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
'dateData.mode': {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
immediate: true
|
||||
},
|
||||
trendData: {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
formatList() {
|
||||
const { mode } = this.dateData
|
||||
const flatList = this.trendData.flat().filter(item => item.time) // 扁平化+过滤无效数据
|
||||
|
||||
// 处理数据:同时提取formattedTime、value
|
||||
this.formattedList = flatList.map(item => ({
|
||||
...item,
|
||||
formattedTime: this.formatTime(item.time, mode)
|
||||
}))
|
||||
|
||||
// 提取value数组(原逻辑不变)
|
||||
this.seriesData = this.formattedList.map(item => item.value)
|
||||
this.name = this.formattedList.length > 0 ? this.formattedList[0].name : ''
|
||||
// 新增:提取格式化后的时间到timeArray(与valueArray顺序一一对应)
|
||||
this.xData = this.formattedList.map(item => item.formattedTime)
|
||||
},
|
||||
|
||||
formatTime(timestamp, mode) {
|
||||
const date = new Date(timestamp)
|
||||
const padZero = (num) => num.toString().padStart(2, '0')
|
||||
|
||||
switch (mode) {
|
||||
case 1:
|
||||
// mode=1:时分秒(HH:MM:SS)
|
||||
return `${padZero(date.getHours())}:${padZero(date.getMinutes())}:${padZero(date.getSeconds())}`
|
||||
case 2:
|
||||
// mode=2:月日(MM-DD)
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
case 3:
|
||||
// mode=3:年(YYYY)
|
||||
return date.getFullYear().toString()
|
||||
default:
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ export default {
|
||||
.progress-yield-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.progress-group {
|
||||
|
||||
@@ -256,7 +256,7 @@ export default {
|
||||
.middle-line {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(to right, rgba(40, 203, 151, 0.3), rgba(40, 203, 151, 0.8), rgba(40, 203, 151, 0.3));
|
||||
background: linear-gradient(to right, #cbcbcb rgba(255, 255, 255, 0));
|
||||
}
|
||||
|
||||
.number {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<!-- 按钮单独放在右侧容器 -->
|
||||
<div class="button-wrapper">
|
||||
<el-button @click="handleRoute" type="text">利润影响额</el-button>
|
||||
<el-button style="border-color: #0b58ff;" @click="handleRoute">利润影响额</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-body">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div style="display: flex; width: 100%; background-color: rgba(249, 252, 255, 1);">
|
||||
<costBaseBarChart />
|
||||
<costBaseBarChart :formattedList="formattedList" :seriesData="seriesData" :xData="xData" :name="'单耗趋势'" />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
components: { Container, costBaseBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
unitConsumptionList: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
@@ -29,154 +29,67 @@ export default {
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
parentItemList: [
|
||||
{ unit: "利润总额", targetValue: 16, currentValue: 14.5, progress: 90 },
|
||||
{ unit: "毛利率", targetValue: 16, currentValue: 15.2, progress: 85 },
|
||||
{ unit: "单价", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "净价", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "销量", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "双镀面板", targetValue: 15, currentValue: 13.8, progress: 92 },
|
||||
{ unit: "溢价产品销量", targetValue: 15, currentValue: 13.8, progress: 92 }
|
||||
]
|
||||
formattedList: [], // 格式化后的完整数据(含处理后的time)
|
||||
seriesData: [], // value数组
|
||||
xData: [] // 新增:存储格式化后的时间数组
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
'dateData.mode': {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
immediate: true
|
||||
},
|
||||
unitConsumptionList: {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
formatList() {
|
||||
const { mode } = this.dateData
|
||||
const flatList = this.unitConsumptionList.flat().filter(item => item.time) // 扁平化+过滤无效数据
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
// 处理数据:同时提取formattedTime、value
|
||||
this.formattedList = flatList.map(item => ({
|
||||
...item,
|
||||
formattedTime: this.formatTime(item.time, mode)
|
||||
}))
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
// 提取value数组(原逻辑不变)
|
||||
this.seriesData = this.formattedList.map(item => item.value)
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
// 新增:提取格式化后的时间到timeArray(与valueArray顺序一一对应)
|
||||
this.xData = this.formattedList.map(item => item.formattedTime)
|
||||
},
|
||||
|
||||
formatTime(timestamp, mode) {
|
||||
const date = new Date(timestamp)
|
||||
const padZero = (num) => num.toString().padStart(2, '0')
|
||||
|
||||
switch (mode) {
|
||||
case 1:
|
||||
// mode=1:时分秒(HH:MM:SS)
|
||||
return `${padZero(date.getHours())}:${padZero(date.getMinutes())}:${padZero(date.getSeconds())}`
|
||||
case 2:
|
||||
// mode=2:月日(MM-DD)
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
case 3:
|
||||
// mode=3:年(YYYY)
|
||||
return date.getFullYear().toString()
|
||||
default:
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
}
|
||||
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div style="display: flex; width: 100%; background-color: rgba(249, 252, 255, 1);">
|
||||
<costBaseBarChart />
|
||||
<costBaseBarChart :formattedList="formattedList" :seriesData="seriesData" :xData="xData" :name="'产量'" />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
components: { Container, costBaseBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
outputList: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
@@ -29,154 +29,67 @@ export default {
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
parentItemList: [
|
||||
{ unit: "利润总额", targetValue: 16, currentValue: 14.5, progress: 90 },
|
||||
{ unit: "毛利率", targetValue: 16, currentValue: 15.2, progress: 85 },
|
||||
{ unit: "单价", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "净价", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "销量", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "双镀面板", targetValue: 15, currentValue: 13.8, progress: 92 },
|
||||
{ unit: "溢价产品销量", targetValue: 15, currentValue: 13.8, progress: 92 }
|
||||
]
|
||||
formattedList: [], // 格式化后的完整数据(含处理后的time)
|
||||
seriesData: [], // value数组
|
||||
xData: [] // 新增:存储格式化后的时间数组
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
'dateData.mode': {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
immediate: true
|
||||
},
|
||||
outputList: {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
formatList() {
|
||||
const { mode } = this.dateData
|
||||
const flatList = this.outputList.flat().filter(item => item.time) // 扁平化+过滤无效数据
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
// 处理数据:同时提取formattedTime、value
|
||||
this.formattedList = flatList.map(item => ({
|
||||
...item,
|
||||
formattedTime: this.formatTime(item.time, mode)
|
||||
}))
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
// 提取value数组(原逻辑不变)
|
||||
this.seriesData = this.formattedList.map(item => item.value)
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
// 新增:提取格式化后的时间到timeArray(与valueArray顺序一一对应)
|
||||
this.xData = this.formattedList.map(item => item.formattedTime)
|
||||
},
|
||||
|
||||
formatTime(timestamp, mode) {
|
||||
const date = new Date(timestamp)
|
||||
const padZero = (num) => num.toString().padStart(2, '0')
|
||||
|
||||
switch (mode) {
|
||||
case 1:
|
||||
// mode=1:时分秒(HH:MM:SS)
|
||||
return `${padZero(date.getHours())}:${padZero(date.getMinutes())}:${padZero(date.getSeconds())}`
|
||||
case 2:
|
||||
// mode=2:月日(MM-DD)
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
case 3:
|
||||
// mode=3:年(YYYY)
|
||||
return date.getFullYear().toString()
|
||||
default:
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
}
|
||||
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,48 @@ import * as echarts from 'echarts';
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {};
|
||||
return {
|
||||
myChart: null, // 存储图表实例,避免重复创建
|
||||
resizeTimer: null // 防抖定时器,优化窗口缩放
|
||||
};
|
||||
},
|
||||
props: {
|
||||
seriesData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
xData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: () => {}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 监听 xData 变化,触发图表更新
|
||||
xData: {
|
||||
handler() {
|
||||
this.$nextTick(() => this.initData());
|
||||
},
|
||||
deep: true // 深度监听数组内元素变化
|
||||
},
|
||||
// 监听 seriesData 变化,触发图表更新
|
||||
seriesData: {
|
||||
handler() {
|
||||
this.$nextTick(() => this.initData());
|
||||
},
|
||||
deep: true // 深度监听数组内元素变化
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initData();
|
||||
});
|
||||
// 组件挂载时初始化图表
|
||||
this.$nextTick(() => this.initData());
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源,避免内存泄漏
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
@@ -22,8 +58,26 @@ export default {
|
||||
console.error('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
const myChart = echarts.init(chartDom);
|
||||
const option = {
|
||||
|
||||
// 销毁已有实例,避免重复创建(关键优化)
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
}
|
||||
|
||||
// 初始化图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
const option = this.getChartOption(); // 抽离配置项,便于维护
|
||||
|
||||
// 设置配置项渲染图表
|
||||
this.myChart.setOption(option, true); // 第二个参数 true 表示全量更新
|
||||
|
||||
// 绑定窗口缩放事件(防抖优化)
|
||||
this.bindResizeEvent();
|
||||
},
|
||||
|
||||
// 抽离图表配置项,提高可维护性
|
||||
getChartOption() {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
@@ -31,27 +85,18 @@ export default {
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
},
|
||||
// 优化tooltip内容,区分各系列含义
|
||||
formatter: (params) => {
|
||||
let html = `${params[0].axisValue}<br/>`;
|
||||
params.forEach(item => {
|
||||
// 直接使用系列名,无需映射,仅保留单位判断
|
||||
html += `${item.marker} ${item.seriesName}: ${item.value}${item.seriesName === '完成率' ? '%' : '片'}<br/>`;
|
||||
});
|
||||
return html;
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 50,
|
||||
bottom: 30, // 增大底部间距,避免柱子与X轴标签重叠
|
||||
bottom: 30,
|
||||
right: 70,
|
||||
left: 50,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true, // 开启边界间隙,让柱子居中显示,不贴边
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
@@ -61,24 +106,22 @@ export default {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0] // 标签向下偏移,避免与柱子底部重叠
|
||||
padding: [5, 0, 0, 0]
|
||||
},
|
||||
data: ['6月', '7月', '8月', '9月', '10月', '11月']
|
||||
data: this.xData // 绑定监听的 xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
// 左侧Y轴:目标/达标/未达标(数量,单位“片”)
|
||||
{
|
||||
type: 'value',
|
||||
// name: '元/吨',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
min: 0, // 最小值设0,确保柱子从X轴底部开始,不超过X轴
|
||||
max: (value) => Math.ceil(value.max * 1.1), // 最大值留10%余量,避免柱子顶满
|
||||
scale: false, // 关闭缩放,强制从0开始
|
||||
min: 0,
|
||||
max: (value) => Math.ceil((value.max || 1) * 1.1), // 处理 max 为 0 的情况
|
||||
scale: false,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
@@ -88,65 +131,30 @@ export default {
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
},
|
||||
// 右侧Y轴:完成率(百分比)
|
||||
// {
|
||||
// type: 'value',
|
||||
// // name: '%',
|
||||
// nameTextStyle: {
|
||||
// color: 'rgba(0, 0, 0, 0.45)',
|
||||
// fontSize: 12,
|
||||
// align: 'left'
|
||||
// },
|
||||
// min: 0,
|
||||
// max: 100, // 完成率最大100%,符合业务逻辑
|
||||
// axisTick: { show: false },
|
||||
// axisLabel: {
|
||||
// color: 'rgba(0, 0, 0, 0.45)',
|
||||
// fontSize: 12,
|
||||
// formatter: '{value}%'
|
||||
// },
|
||||
// splitLine: { show: false }, // 不重复显示分割线
|
||||
// axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
// splitNumber: 4
|
||||
// }
|
||||
}
|
||||
],
|
||||
series: [
|
||||
// 1. 完成率:折线图(绑定右侧百分比Y轴)
|
||||
// {
|
||||
// name: '产销率',
|
||||
// type: 'line',
|
||||
// yAxisIndex: 1, // 绑定右侧Y轴
|
||||
// lineStyle: {
|
||||
// color: 'rgba(40, 138, 255, .5)',
|
||||
// width: 2 // 线条加粗,突出重点
|
||||
// },
|
||||
// itemStyle: {
|
||||
// color: 'rgba(40, 138, 255, 1)',
|
||||
// borderColor: 'rgba(40, 138, 255, 1)', // 数据点白色边框,增强辨识度
|
||||
// borderWidth: 2,
|
||||
// radius: 4 // 数据点圆角,更圆润
|
||||
// },
|
||||
// areaStyle: {
|
||||
// opacity: 0.2, // 降低面积透明度,不抢柱状图视觉焦点
|
||||
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
// { offset: 0, color: 'rgba(40, 138, 255, .9)' },
|
||||
// { offset: 1, color: 'rgba(255, 132, 0, 0)' }
|
||||
// ])
|
||||
// },
|
||||
// data: [65, 78, 52, 85, 60, 95, 72], // 完成率数据(0-100)
|
||||
// symbol: 'circle', // 数据点为圆形
|
||||
// symbolSize: 6 // 数据点大小
|
||||
// },
|
||||
// 2. 目标:柱状图(绑定左侧数量Y轴)
|
||||
{
|
||||
name: '销量',
|
||||
name: this.name,
|
||||
type: 'bar',
|
||||
yAxisIndex: 0,
|
||||
barWidth: 24,
|
||||
// 关键修复:label 直接放在 series 下,而非 itemStyle 内
|
||||
label: {
|
||||
show: true, // 开启显示
|
||||
position: 'top', // 标签位置,可选:'top'、'middle'、'bottom'
|
||||
// 也可以使用绝对像素值定位,例如 [10, '50%']
|
||||
// position: [10, '50%'],
|
||||
|
||||
// 标签内容格式化,这里直接显示数据值
|
||||
formatter: '{c}',
|
||||
|
||||
// 文字样式
|
||||
color: 'rgba(11, 88, 255, 1)', // 文字颜色
|
||||
fontSize: 14, // 文字大小
|
||||
// fontWeight: 'bold', // 文字粗细
|
||||
// fontFamily: 'Arial, sans-serif' // 字体
|
||||
},
|
||||
itemStyle: {
|
||||
// 移除多余的 normal 层级,直接配置 color 渐变
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
@@ -161,76 +169,37 @@ export default {
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
borderWidth: 0
|
||||
},
|
||||
data: [200, 280, 180, 300, 220, 350]
|
||||
},
|
||||
// 3. 达标:柱状图(绑定左侧数量Y轴)
|
||||
// {
|
||||
// name: '产量',
|
||||
// type: 'bar',
|
||||
// yAxisIndex: 0,
|
||||
// barWidth: 24,
|
||||
// // 关键修复:label 直接放在 series 下,而非 itemStyle 内
|
||||
// itemStyle: {
|
||||
// // 移除多余的 normal 层级,直接配置 color 渐变
|
||||
// color: {
|
||||
// type: 'linear',
|
||||
// x: 0,
|
||||
// y: 0,
|
||||
// x2: 0,
|
||||
// y2: 1,
|
||||
// colorStops: [
|
||||
// { offset: 0, color: 'rgba(174, 239, 224, 1)' },
|
||||
// { offset: 1, color: 'rgba(118, 218, 190, 1)' }
|
||||
// ]
|
||||
// },
|
||||
// borderRadius: [4, 4, 0, 0],
|
||||
// borderWidth: 0
|
||||
// },
|
||||
// data: [130, 220, 95, 255, 132, 332] // 达标数据(小于目标)
|
||||
// },
|
||||
// 4. 未达标:柱状图(绑定左侧数量Y轴)
|
||||
// {
|
||||
// name: '未达标',
|
||||
// type: 'bar',
|
||||
// yAxisIndex: 0,
|
||||
// barWidth: 18,
|
||||
// itemStyle: {
|
||||
// color: 'rgba(249, 164, 74, 1)',
|
||||
// borderRadius: [4, 4, 0, 0],
|
||||
// borderWidth: 0
|
||||
// },
|
||||
// data: [70, 60, 85, 45, 88, 18, 78] // 未达标数据(目标-达标)
|
||||
// }
|
||||
],
|
||||
// 图例:区分各系列,点击可控制显示隐藏
|
||||
// legend: {
|
||||
// top: 0,
|
||||
// left: 'center',
|
||||
// itemWidth: 12,
|
||||
// itemHeight: 8,
|
||||
// textStyle: {
|
||||
// color: 'rgba(0, 0, 0, 0.45)',
|
||||
// fontSize: 12
|
||||
// },
|
||||
// data: ['完成率', '目标', '达标', '未达标']
|
||||
// }
|
||||
data: this.seriesData // 绑定监听的 seriesData
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
option && myChart.setOption(option);
|
||||
// 绑定窗口缩放事件(防抖处理,避免频繁触发)
|
||||
bindResizeEvent() {
|
||||
window.removeEventListener('resize', this.handleResize); // 先移除旧事件,避免重复绑定
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
},
|
||||
|
||||
// 窗口缩放适配
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
// 窗口缩放处理函数(防抖)
|
||||
handleResize() {
|
||||
clearTimeout(this.resizeTimer);
|
||||
this.resizeTimer = setTimeout(() => {
|
||||
if (this.myChart) {
|
||||
this.myChart.resize();
|
||||
}
|
||||
}, 200);
|
||||
},
|
||||
|
||||
// 组件销毁清理
|
||||
this.$once('hook:destroyed', () => {
|
||||
window.removeEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
myChart.dispose();
|
||||
});
|
||||
// 销毁图表实例和事件监听
|
||||
destroyChart() {
|
||||
if (this.myChart) {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
clearTimeout(this.resizeTimer);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -111,44 +111,40 @@ export default {
|
||||
const mode = this.activeTime + 1; // 1=日,2=月,3=年
|
||||
const defaultMoment = moment(); // 默认当前时间
|
||||
|
||||
// 处理选择的日期:转为moment对象(兼容不同选择器格式)
|
||||
console.log('this.date', this.date);
|
||||
|
||||
const targetMoment = this.date
|
||||
? moment(this.date, this.getPickerType === 'date' ? 'YYYY-MM-DD' : (this.getPickerType === 'month' ? 'YYYY-MM' : 'YYYY'))
|
||||
: defaultMoment;
|
||||
|
||||
// 验证日期有效性
|
||||
if (!targetMoment.isValid()) {
|
||||
console.error('无效日期:', this.date);
|
||||
return { startTime, endTime, mode };
|
||||
}
|
||||
|
||||
// 1. 日维度:当天0点 → 次日0点
|
||||
// 1. 日维度:00:00:00 → 23:59:59(无毫秒)
|
||||
if (this.activeTime === 0) {
|
||||
startTime = targetMoment.startOf('day').valueOf(); // 当天00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'day').startOf('day').valueOf(); // 次日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('day').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('day').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 2. 月维度:当月1日0点 → 次月1日0点
|
||||
// 2. 月维度:当月1日00:00:00 → 当月最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 1) {
|
||||
startTime = targetMoment.startOf('month').valueOf(); // 当月1日00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'month').startOf('month').valueOf(); // 次月1日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('month').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('month').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 3. 年维度:当年1月1日0点 → 次年1月1日0点
|
||||
// 3. 年维度:当年1月1日00:00:00 → 当年最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 2) {
|
||||
startTime = targetMoment.startOf('year').valueOf(); // 当年1月1日00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'year').startOf('year').valueOf(); // 次年1月1日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('year').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('year').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 调试输出(格式化显示,便于验证)
|
||||
// 调试输出:验证是否去掉毫秒
|
||||
console.log('时间范围计算结果:', {
|
||||
mode,
|
||||
startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
startTimeStamp: startTime,
|
||||
endTimeStamp: endTime
|
||||
startTime: moment(startTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 00:00:00
|
||||
endTime: moment(endTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 23:59:59(无毫秒)
|
||||
startTimeStamp: startTime, // 秒级时间戳(如:1764422400)
|
||||
endTimeStamp: endTime // 秒级时间戳(如:1764508799)
|
||||
});
|
||||
|
||||
return { startTime, endTime, mode };
|
||||
|
||||
@@ -110,44 +110,40 @@ export default {
|
||||
const mode = this.activeTime + 1; // 1=日,2=月,3=年
|
||||
const defaultMoment = moment(); // 默认当前时间
|
||||
|
||||
// 处理选择的日期:转为moment对象(兼容不同选择器格式)
|
||||
console.log('this.date', this.date);
|
||||
|
||||
const targetMoment = this.date
|
||||
? moment(this.date, this.getPickerType === 'date' ? 'YYYY-MM-DD' : (this.getPickerType === 'month' ? 'YYYY-MM' : 'YYYY'))
|
||||
: defaultMoment;
|
||||
|
||||
// 验证日期有效性
|
||||
if (!targetMoment.isValid()) {
|
||||
console.error('无效日期:', this.date);
|
||||
return { startTime, endTime, mode };
|
||||
}
|
||||
|
||||
// 1. 日维度:当天0点 → 次日0点
|
||||
// 1. 日维度:00:00:00 → 23:59:59(无毫秒)
|
||||
if (this.activeTime === 0) {
|
||||
startTime = targetMoment.startOf('day').valueOf(); // 当天00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'day').startOf('day').valueOf(); // 次日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('day').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('day').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 2. 月维度:当月1日0点 → 次月1日0点
|
||||
// 2. 月维度:当月1日00:00:00 → 当月最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 1) {
|
||||
startTime = targetMoment.startOf('month').valueOf(); // 当月1日00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'month').startOf('month').valueOf(); // 次月1日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('month').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('month').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 3. 年维度:当年1月1日0点 → 次年1月1日0点
|
||||
// 3. 年维度:当年1月1日00:00:00 → 当年最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 2) {
|
||||
startTime = targetMoment.startOf('year').valueOf(); // 当年1月1日00:00:00 时间戳
|
||||
endTime = targetMoment.add(1, 'year').startOf('year').valueOf(); // 次年1月1日00:00:00 时间戳
|
||||
startTime = targetMoment.startOf('year').millisecond(0).valueOf();
|
||||
endTime = targetMoment.endOf('year').millisecond(0).valueOf();
|
||||
}
|
||||
|
||||
// 调试输出(格式化显示,便于验证)
|
||||
// 调试输出:验证是否去掉毫秒
|
||||
console.log('时间范围计算结果:', {
|
||||
mode,
|
||||
startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
startTimeStamp: startTime,
|
||||
endTimeStamp: endTime
|
||||
startTime: moment(startTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 00:00:00
|
||||
endTime: moment(endTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 23:59:59(无毫秒)
|
||||
startTimeStamp: startTime, // 秒级时间戳(如:1764422400)
|
||||
endTimeStamp: endTime // 秒级时间戳(如:1764508799)
|
||||
});
|
||||
|
||||
return { startTime, endTime, mode };
|
||||
|
||||
@@ -1,353 +1,96 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container :name="name" icon="cockpitItemIcon" size="operatingBasic" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div style="display: flex; width: 100%; background-color: rgba(249, 252, 255, 1);">
|
||||
<costBaseBarChart />
|
||||
<costBaseBarChart :formattedList="formattedList" :seriesData="seriesData" :xData="xData" :name="'采购单价' " />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Container from '../single-container.vue'
|
||||
// import * as echarts from 'echarts'
|
||||
import costBaseBarChart from './costBaseBarChart.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, costBaseBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
pupList: {
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
default: () => []
|
||||
},
|
||||
name: { // 接收父组件传递的设备数据数组
|
||||
name: {
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
default: ''
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
parentItemList: [
|
||||
{ unit: "利润总额", targetValue: 16, currentValue: 14.5, progress: 90 },
|
||||
{ unit: "毛利率", targetValue: 16, currentValue: 15.2, progress: 85 },
|
||||
{ unit: "单价", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "净价", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "销量", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "双镀面板", targetValue: 15, currentValue: 13.8, progress: 92 },
|
||||
{ unit: "溢价产品销量", targetValue: 15, currentValue: 13.8, progress: 92 }
|
||||
]
|
||||
formattedList: [], // 格式化后的完整数据(含处理后的time)
|
||||
seriesData: [], // value数组
|
||||
xData: [] // 新增:存储格式化后的时间数组
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
'dateData.mode': {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
immediate: true
|
||||
},
|
||||
pupList: {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
formatList() {
|
||||
const { mode } = this.dateData
|
||||
const flatList = this.pupList.flat().filter(item => item.time) // 扁平化+过滤无效数据
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
// 处理数据:同时提取formattedTime、value
|
||||
this.formattedList = flatList.map(item => ({
|
||||
...item,
|
||||
formattedTime: this.formatTime(item.time, mode)
|
||||
}))
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
// 提取value数组(原逻辑不变)
|
||||
this.seriesData = this.formattedList.map(item => item.value)
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
// 新增:提取格式化后的时间到timeArray(与valueArray顺序一一对应)
|
||||
this.xData = this.formattedList.map(item => item.formattedTime)
|
||||
},
|
||||
|
||||
formatTime(timestamp, mode) {
|
||||
const date = new Date(timestamp)
|
||||
const padZero = (num) => num.toString().padStart(2, '0')
|
||||
|
||||
switch (mode) {
|
||||
case 1:
|
||||
// mode=1:时分秒(HH:MM:SS)
|
||||
return `${padZero(date.getHours())}:${padZero(date.getMinutes())}:${padZero(date.getSeconds())}`
|
||||
case 2:
|
||||
// mode=2:月日(MM-DD)
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
case 3:
|
||||
// mode=3:年(YYYY)
|
||||
return date.getFullYear().toString()
|
||||
default:
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
}
|
||||
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
|
||||
.scroll-container {
|
||||
/* 1. 固定容器高度:根据页面布局调整(示例300px,超出则滚动) */
|
||||
max-height: 210px;
|
||||
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
|
||||
overflow-y: auto;
|
||||
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
|
||||
overflow-x: hidden;
|
||||
/* 4. 内边距:与标题栏和容器边缘对齐 */
|
||||
padding: 10px 0;
|
||||
|
||||
/* 5. 隐藏滚动条(兼容主流浏览器) */
|
||||
/* Chrome/Safari */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* IE/Edge */
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
/* 设备项样式优化:增加间距,避免拥挤 */
|
||||
.proBarInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 8px 27px;
|
||||
/* 调整内边距,优化排版 */
|
||||
margin-bottom: 10px;
|
||||
/* 设备项之间的垂直间距 */
|
||||
}
|
||||
|
||||
/* 原有样式保留,优化细节 */
|
||||
.proBarInfoEqInfo {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
/* 垂直居中,避免序号/文字错位 */
|
||||
}
|
||||
|
||||
.slot {
|
||||
width: 21px;
|
||||
height: 23px;
|
||||
background: rgba(0, 106, 205, 0.22);
|
||||
backdrop-filter: blur(1.5px);
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #68B5FF;
|
||||
line-height: 23px;
|
||||
/* 垂直居中文字 */
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.eq-name {
|
||||
margin-left: 8px;
|
||||
/* 增加与序号的间距 */
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #FFFFFF;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.eqStatus {
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #FFFFFF;
|
||||
line-height: 18px;
|
||||
text-align: right;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.splitLine {
|
||||
width: 1px;
|
||||
height: 14px;
|
||||
border: 1px solid #ADADAD;
|
||||
margin: 0 8px;
|
||||
/* 优化分割线间距 */
|
||||
}
|
||||
|
||||
.yield {
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #00FFFF;
|
||||
line-height: 18px;
|
||||
text-align: right;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.proBarInfoEqInfoLeft {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* 序号和设备名垂直居中 */
|
||||
}
|
||||
|
||||
.proBarInfoEqInfoRight {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* 状态/分割线/百分比垂直居中 */
|
||||
}
|
||||
|
||||
.proBarWrapper {
|
||||
position: relative;
|
||||
height: 10px;
|
||||
margin-top: 6px;
|
||||
/* 进度条与上方信息的间距 */
|
||||
border-radius: 5px;
|
||||
/* 进度条圆角,优化视觉 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.proBarLine {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(65deg, rgba(82, 82, 82, 0) 0%, #ACACAC 100%);
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.proBarLineTop {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
background: linear-gradient(65deg, rgba(53, 223, 247, 0) 0%, rgba(54, 220, 246, 0.92) 92%, #36F6E5 100%, #37ACF5 100%);
|
||||
border-radius: 5px;
|
||||
transition: width 0.3s ease;
|
||||
/* 进度变化时添加过渡动画,更流畅 */
|
||||
}
|
||||
|
||||
/* 图表相关样式保留 */
|
||||
.chartImgBottom {
|
||||
position: absolute;
|
||||
bottom: 45px;
|
||||
left: 58px;
|
||||
}
|
||||
|
||||
.line {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 57px;
|
||||
bottom: 42px;
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
background-color: #00E8FF;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div style="display: flex; width: 100%; background-color: rgba(249, 252, 255, 1);">
|
||||
<costBaseBarChart />
|
||||
<costBaseBarChart :formattedList="formattedList" :seriesData="seriesData" :name="'消耗量' " :xData="xData" />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
@@ -21,162 +21,79 @@ export default {
|
||||
components: { Container, costBaseBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
consumptionList: {
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
default: () => []
|
||||
},
|
||||
name: { // 接收父组件传递的设备数据数组
|
||||
name: {
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
default: ''
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
parentItemList: [
|
||||
{ unit: "利润总额", targetValue: 16, currentValue: 14.5, progress: 90 },
|
||||
{ unit: "毛利率", targetValue: 16, currentValue: 15.2, progress: 85 },
|
||||
{ unit: "单价", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "净价", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "销量", targetValue: 20, currentValue: 16, progress: 80 },
|
||||
{ unit: "双镀面板", targetValue: 15, currentValue: 13.8, progress: 92 },
|
||||
{ unit: "溢价产品销量", targetValue: 15, currentValue: 13.8, progress: 92 }
|
||||
]
|
||||
formattedList: [], // 格式化后的完整数据(含处理后的time)
|
||||
seriesData: [], // value数组
|
||||
xData: [] // 新增:存储格式化后的时间数组
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
'dateData.mode': {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
immediate: true
|
||||
},
|
||||
consumptionList: {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
formatList() {
|
||||
const { mode } = this.dateData
|
||||
const flatList = this.consumptionList.flat().filter(item => item.time) // 扁平化+过滤无效数据
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
// 处理数据:同时提取formattedTime、value
|
||||
this.formattedList = flatList.map(item => ({
|
||||
...item,
|
||||
formattedTime: this.formatTime(item.time, mode)
|
||||
}))
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
// 提取value数组(原逻辑不变)
|
||||
this.seriesData = this.formattedList.map(item => item.value)
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
// 新增:提取格式化后的时间到timeArray(与valueArray顺序一一对应)
|
||||
this.xData = this.formattedList.map(item => item.formattedTime)
|
||||
},
|
||||
|
||||
formatTime(timestamp, mode) {
|
||||
const date = new Date(timestamp)
|
||||
const padZero = (num) => num.toString().padStart(2, '0')
|
||||
|
||||
switch (mode) {
|
||||
case 1:
|
||||
// mode=1:时分秒(HH:MM:SS)
|
||||
return `${padZero(date.getHours())}:${padZero(date.getMinutes())}:${padZero(date.getSeconds())}`
|
||||
case 2:
|
||||
// mode=2:月日(MM-DD)
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
case 3:
|
||||
// mode=3:年(YYYY)
|
||||
return date.getFullYear().toString()
|
||||
default:
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
}
|
||||
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,58 +14,91 @@
|
||||
<div class="label">
|
||||
{{ typeName }}
|
||||
</div>
|
||||
<el-select class="custom-select" style="margin-left: 10px;" v-model="type" placeholder="">
|
||||
<el-option v-for="item in TypeList" :key="item.value" :label="item.name" :value="item.value">
|
||||
<el-select class="custom-select" style="margin-left: 10px;" v-model="selectedType" placeholder="">
|
||||
<el-option v-for="item in typeList" :key="item.value" :label="item.name" :value="item.name">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button type="primary" style="margin-left: 24px;">查询</el-button>
|
||||
<!-- 绑定查询点击事件 -->
|
||||
<el-button type="primary" style="margin-left: 24px;background-color: #0b58ff;letter-spacing: 6px;"
|
||||
@click="handleQuery">
|
||||
查询
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
name: 'singleTopSelect',
|
||||
components: {},
|
||||
props: ['typeName','typeList'],
|
||||
props: {
|
||||
// 类型名称(如"原料"、"燃动力")
|
||||
typeName: {
|
||||
type: String,
|
||||
// required: true,
|
||||
default: () => '',
|
||||
},
|
||||
// 类型列表([{id, name}] 或 [{value, name}])
|
||||
typeList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
validator: val => val.every(item => item.name && (item.id || item.value)) // 校验列表格式
|
||||
},
|
||||
// 默认选中的基地ID(支持父组件传入)
|
||||
defaultLevelId: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 默认选中的类型名称(支持父组件传入)
|
||||
defaultType: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听父组件传入的默认值变化,同步更新
|
||||
// defaultLevelId: {
|
||||
// handler(val) {
|
||||
// this.levelId = val;
|
||||
// },
|
||||
// immediate: true
|
||||
// },
|
||||
defaultType: {
|
||||
handler(val) {
|
||||
this.selectedType = val;
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
levelId: '',
|
||||
levelId: 1,
|
||||
selectedType: '', // 初始化type变量
|
||||
baseList: [
|
||||
{
|
||||
value: 2,
|
||||
name: "宜兴",
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
name: "漳州",
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
name: "自贡",
|
||||
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
name: "桐城",
|
||||
},
|
||||
{
|
||||
value: 6,
|
||||
name: "洛阳",
|
||||
},
|
||||
{
|
||||
value: 7,
|
||||
name: "合肥",
|
||||
},
|
||||
{ value: 1, name: "总览" },
|
||||
{ value: 2, name: "宜兴" },
|
||||
{ value: 3, name: "漳州" },
|
||||
{ value: 4, name: "自贡" },
|
||||
{ value: 5, name: "桐城" },
|
||||
{ value: 6, name: "洛阳" },
|
||||
{ value: 7, name: "合肥" },
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() { }
|
||||
updateChart() { },
|
||||
// 处理查询事件
|
||||
handleQuery() {
|
||||
// 触发自定义事件,将type和levelId传递给父组件
|
||||
this.$emit('query', {
|
||||
type: this.type,
|
||||
levelId: this.levelId
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.topSelect {
|
||||
width: 1620px;
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
||||
<div class=""
|
||||
style="display: flex;width: 100%; background-color: rgba(249, 252, 255, 1);">
|
||||
<div class="" style="display: flex;width: 100%; background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- <topItem :itemList="parentItemList" /> -->
|
||||
<totalProfitBar />
|
||||
<totalProfitBar :name="name" :seriesData="seriesData" :xData="xData" />
|
||||
|
||||
</div>
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
@@ -28,153 +27,72 @@ export default {
|
||||
components: { Container,totalProfitBar },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
leftEqInfoData: { // 接收父组件传递的设备数据数组
|
||||
totalProfitIndict: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
productionOverviewVo: { // 恢复生产概览数据(原代码注释了,需根据实际需求保留)
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
parentItemList: [
|
||||
{ unit: "销量", targetValue: 16, currentValue: 14.5, progress: 90 },
|
||||
{ unit: "产量", targetValue: 16, currentValue: 15.2, progress: 85 },
|
||||
]
|
||||
name:'',
|
||||
formattedList: [], // 格式化后的完整数据(含处理后的time)
|
||||
seriesData: [], // value数组
|
||||
xData: [] // 新增:存储格式化后的时间数组
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
'dateData.mode': {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 销毁图表,避免内存泄漏
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
immediate: true
|
||||
},
|
||||
totalProfitIndict: {
|
||||
handler() {
|
||||
this.formatList()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
// 注意:原代码中图表依赖 id 为 "productionStatusChart" 的 DOM,需在模板中补充,否则会报错
|
||||
// 示例:在 Container 内添加 <div id="productionStatusChart" style="height: 200px;"></div>
|
||||
if (!document.getElementById('productionStatusChart')) return
|
||||
formatList() {
|
||||
const { mode } = this.dateData
|
||||
const flatList = this.totalProfitIndict.flat().filter(item => item.time) // 扁平化+过滤无效数据
|
||||
|
||||
if (this.chart) this.chart.dispose()
|
||||
this.chart = echarts.init(document.getElementById('productionStatusChart'))
|
||||
// 处理数据:同时提取formattedTime、value
|
||||
this.formattedList = flatList.map(item => ({
|
||||
...item,
|
||||
formattedTime: this.formatTime(item.time, mode)
|
||||
}))
|
||||
|
||||
const data = [
|
||||
this.productionOverviewVo.input || 0,
|
||||
this.productionOverviewVo.output || 0,
|
||||
this.productionOverviewVo.ng || 0,
|
||||
this.productionOverviewVo.lowValue || 0,
|
||||
this.productionOverviewVo.scrap || 0,
|
||||
this.productionOverviewVo.inProcess || 0,
|
||||
this.productionOverviewVo.engineer || 0
|
||||
]
|
||||
// 提取value数组(原逻辑不变)
|
||||
this.seriesData = this.formattedList.map(item => item.value)
|
||||
this.name = this.formattedList.length > 0 ? this.formattedList[0].name : ''
|
||||
// 新增:提取格式化后的时间到timeArray(与valueArray顺序一一对应)
|
||||
this.xData = this.formattedList.map(item => item.formattedTime)
|
||||
},
|
||||
|
||||
const option = {
|
||||
type: 'bar',
|
||||
grid: { left: 51, right: 40, top: 50, bottom: 45 },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
className: 'production-status-chart-tooltip'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 8,
|
||||
data: ['投入', '产出', '待判', '低价值', '报废', '在制', '实验片'],
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: true, onZero: false, lineStyle: { color: '#00E8FF' } },
|
||||
axisLabel: {
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
width: 38,
|
||||
overflow: 'break'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位/片',
|
||||
nameTextStyle: { color: 'rgba(255,255,255,0.7)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'RGBA(24, 88, 100, 0.6)', type: 'dashed' } },
|
||||
axisLine: { show: true, lineStyle: { color: '#00E8FF' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
label: { show: true, position: 'top', distance: -3, color: '#89CDFF', fontSize: 11 },
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, 5],
|
||||
z: 20,
|
||||
itemStyle: {
|
||||
borderColor: '#3588C7',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#3588C7',
|
||||
opacity: 0.8,
|
||||
color: {
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
type: 'linear',
|
||||
global: false,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(73,178,255,0)' },
|
||||
{ offset: 0.5, color: 'rgba(0, 232, 255, .5)' },
|
||||
{ offset: 1, color: 'rgba(0, 232, 255, 1)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
},
|
||||
{
|
||||
type: 'pictorialBar',
|
||||
symbolSize: [20, 8],
|
||||
symbolOffset: [0, -4],
|
||||
z: 12,
|
||||
symbolPosition: 'end',
|
||||
itemStyle: {
|
||||
borderColor: 'rgba(0, 232, 255, 1)',
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'RGBA(22, 89, 98, 1)' },
|
||||
{ offset: 1, color: '#3588C7' }
|
||||
])
|
||||
},
|
||||
tooltip: { show: false },
|
||||
data: data
|
||||
}
|
||||
]
|
||||
formatTime(timestamp, mode) {
|
||||
const date = new Date(timestamp)
|
||||
const padZero = (num) => num.toString().padStart(2, '0')
|
||||
|
||||
switch (mode) {
|
||||
case 1:
|
||||
// mode=1:时分秒(HH:MM:SS)
|
||||
return `${padZero(date.getHours())}:${padZero(date.getMinutes())}:${padZero(date.getSeconds())}`
|
||||
case 2:
|
||||
// mode=2:月日(MM-DD)
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
case 3:
|
||||
// mode=3:年(YYYY)
|
||||
return date.getFullYear().toString()
|
||||
default:
|
||||
return `${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
|
||||
}
|
||||
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,36 @@ export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
props: {
|
||||
seriesData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
xData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: () => { }
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 监听 xData 变化,触发图表更新
|
||||
xData: {
|
||||
handler() {
|
||||
this.$nextTick(() => this.initData());
|
||||
},
|
||||
deep: true // 深度监听数组内元素变化
|
||||
},
|
||||
// 监听 seriesData 变化,触发图表更新
|
||||
seriesData: {
|
||||
handler() {
|
||||
this.$nextTick(() => this.initData());
|
||||
},
|
||||
deep: true // 深度监听数组内元素变化
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initData();
|
||||
@@ -37,7 +67,7 @@ export default {
|
||||
let html = `${params[0].axisValue}<br/>`;
|
||||
params.forEach(item => {
|
||||
// 直接使用系列名,无需映射,仅保留单位判断
|
||||
html += `${item.marker} ${item.seriesName}: ${item.value}${item.seriesName === '完成率' ? '%' : '片'}<br/>`;
|
||||
html += `${item.marker} ${item.seriesName}: ${item.value}${'元'}<br/>`;
|
||||
});
|
||||
return html;
|
||||
}
|
||||
@@ -63,7 +93,7 @@ export default {
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0] // 标签向下偏移,避免与柱子底部重叠
|
||||
},
|
||||
data: ['漳州', '桐城', '合肥', '宜兴', '自贡', '洛阳']
|
||||
data: this.xData // 绑定监听的 xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
@@ -140,11 +170,25 @@ export default {
|
||||
// },
|
||||
// 2. 目标:柱状图(绑定左侧数量Y轴)
|
||||
{
|
||||
name: '销量',
|
||||
name:this.name,
|
||||
type: 'bar',
|
||||
yAxisIndex: 0,
|
||||
barWidth: 24,
|
||||
// 关键修复:label 直接放在 series 下,而非 itemStyle 内
|
||||
label: {
|
||||
show: true, // 开启显示
|
||||
position: 'top', // 标签位置,可选:'top'、'middle'、'bottom'
|
||||
// 也可以使用绝对像素值定位,例如 [10, '50%']
|
||||
// position: [10, '50%'],
|
||||
|
||||
// 标签内容格式化,这里直接显示数据值
|
||||
formatter: '{c}',
|
||||
|
||||
// 文字样式
|
||||
color: 'rgba(11, 88, 255, 1)', // 文字颜色
|
||||
fontSize: 14, // 文字大小
|
||||
// fontWeight: 'bold', // 文字粗细
|
||||
// fontFamily: 'Arial, sans-serif' // 字体
|
||||
},
|
||||
itemStyle: {
|
||||
// 移除多余的 normal 层级,直接配置 color 渐变
|
||||
color: {
|
||||
@@ -161,7 +205,7 @@ export default {
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
borderWidth: 0
|
||||
},
|
||||
data: [200, 280, 180, 300, 220, 350]
|
||||
data: this.seriesData
|
||||
},
|
||||
// 3. 达标:柱状图(绑定左侧数量Y轴)
|
||||
// {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<profitLineChart :yName="'元/㎡'" :trendData="trendData" />
|
||||
<profitLineChart :yName="'元/㎡'" :trendData="trendData" :dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,9 +83,7 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
startTime:undefined,
|
||||
endTime: undefined,
|
||||
mode: undefined,
|
||||
dateData:{},
|
||||
levelId:undefined,
|
||||
itemData: [],
|
||||
trendData: [],
|
||||
@@ -167,14 +165,13 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
// 基于选中的数据构建请求参数(根据实际接口需求调整)
|
||||
const requestParams = {
|
||||
// startTime: this.startTime,
|
||||
// endTime: this.endTime,
|
||||
// mode: this.mode,
|
||||
startTime: 1762704000000,
|
||||
endTime: 1762790400000,
|
||||
mode: 1,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
trendName: "燃料成本",
|
||||
levelId: this.levelId ? this.levelId :1
|
||||
};
|
||||
@@ -192,16 +189,18 @@ export default {
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.startTime = obj.startTime
|
||||
this.endTime = obj.endTime
|
||||
this.mode = obj.mode
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
|
||||
this.getData()
|
||||
},
|
||||
selectChange(data) {
|
||||
console.log('选中的数据:', data);
|
||||
this.levelId = data
|
||||
if (this.startTime && this.endTime) {
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<profitLineChart :yName="'元/㎡'" :trendData="trendData" />
|
||||
<profitLineChart :yName="'元/㎡'" :trendData="trendData" :dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,9 +83,7 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
mode: undefined,
|
||||
dateData:{},
|
||||
levelId: undefined,
|
||||
itemData: [],
|
||||
trendData: [],
|
||||
@@ -172,9 +170,9 @@ export default {
|
||||
// startTime: this.startTime,
|
||||
// endTime: this.endTime,
|
||||
// mode: this.mode,
|
||||
startTime: 1762704000000,
|
||||
endTime: 1762790400000,
|
||||
mode: 1,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
trendName: "燃动力成本",
|
||||
levelId: this.levelId ? this.levelId : 1
|
||||
};
|
||||
@@ -192,16 +190,18 @@ export default {
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.startTime = obj.startTime
|
||||
this.endTime = obj.endTime
|
||||
this.mode = obj.mode
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
|
||||
this.getData()
|
||||
},
|
||||
selectChange(data) {
|
||||
console.log('选中的数据:', data);
|
||||
this.levelId = data
|
||||
if (this.startTime && this.endTime) {
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div id="dayReport" class="dayReport" :style="styles">
|
||||
<ReportHeader top-title="洛玻集团运营驾驶舱" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange"
|
||||
@timeRangeChange="handleTimeChange" />
|
||||
@timeRangeChange="handleTimeChange" />
|
||||
<div class="main-body"
|
||||
style="margin-top: -20px; flex: 1; display: flex;padding: 0px 16px 0;flex-direction: column;">
|
||||
<div class="top" style="display: flex;gap: 16px;">
|
||||
@@ -10,9 +10,9 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 560px 745px 560px ;
|
||||
">
|
||||
<coreSalesKPIs />
|
||||
<financeCosts />
|
||||
<keyProductionIndicators />
|
||||
<coreSalesKPIs :sale="sale" :dateData="dateData" />
|
||||
<financeCosts :finance="finance" :dateData="dateData" :cost="cost" />
|
||||
<keyProductionIndicators :productData="productData" :dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex;gap: 16px;margin-top: 6px;">
|
||||
@@ -21,9 +21,9 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 560px 745px 560px ;
|
||||
">
|
||||
<coreBottomLeft />
|
||||
<orderProgress />
|
||||
<keyWork />
|
||||
<coreBottomLeft :purchase="purchase" :inventory="inventory" />
|
||||
<orderProgress @getData="getOrderData" :baseOrder="orderTableData" :orderOutput="orderOutput" />
|
||||
<keyWork :importantWork="importantWork" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,7 +38,8 @@ import keyProductionIndicators from './components/keyProductionIndicators.vue'
|
||||
import coreBottomLeft from './components/coreBottomLeft.vue'
|
||||
import orderProgress from './components/orderProgress.vue'
|
||||
import keyWork from './components/keyWork.vue'
|
||||
import moment from 'moment'
|
||||
// import moment from 'moment'
|
||||
import { getOperateCockpit, getOrderDetail } from '@/api/cockpit'
|
||||
export default {
|
||||
name: 'DayReport',
|
||||
components: { ReportHeader, coreSalesKPIs, keyProductionIndicators, coreBottomLeft, keyWork, orderProgress, financeCosts },
|
||||
@@ -48,6 +49,17 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
orderTableData:[],
|
||||
productData: {},
|
||||
purchase: {},
|
||||
dateData:{},
|
||||
inventory: {},
|
||||
importantWork: {},
|
||||
finance: {},
|
||||
cost: {},
|
||||
sale: {},
|
||||
orderOutput: {},
|
||||
baseOrder:[],
|
||||
}
|
||||
},
|
||||
|
||||
@@ -96,9 +108,53 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getOrderData(num) {
|
||||
const res = await getOrderDetail({
|
||||
// startTime: this.dateData.startTime,
|
||||
// endTime: this.dateData.endTime,
|
||||
// timeDim: this.dateData.mode,
|
||||
baseId: num
|
||||
})
|
||||
this.orderTableData = res.data
|
||||
},
|
||||
getData(obj) {
|
||||
console.log('obj', obj);
|
||||
|
||||
this.dateData= obj
|
||||
getOperateCockpit({
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
timeDim: obj.mode ? obj.mode : obj.timeDim,
|
||||
baseId: obj.baseId ? obj.baseId : 1
|
||||
}).then((res) => {
|
||||
console.log(res)
|
||||
this.productData = res.data.product
|
||||
this.purchase = res.data.purchase
|
||||
this.inventory = res.data.inventory
|
||||
this.importantWork = res.data.importantWork
|
||||
this.finance = res.data.finance
|
||||
this.cost = res.data.cost
|
||||
this.sale = res.data.sale
|
||||
this.orderOutput = res.data.orderOutput
|
||||
this.baseOrder = res.data.baseOrder
|
||||
// this.saleData = res.data.SaleData
|
||||
// this.premiumProduct = res.data.premiumProduct
|
||||
// this.salesTrendMap = res.data.salesTrendMap
|
||||
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
||||
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
||||
})
|
||||
},
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.getData(obj)
|
||||
},
|
||||
change() {
|
||||
this.isFullScreen = screenfull.isFullscreen
|
||||
},
|
||||
windowWidth(value) {
|
||||
this.clientWidth = value;
|
||||
this.beilv2 = this.clientWidth / 1920;
|
||||
},
|
||||
init() {
|
||||
if (!screenfull.isEnabled) {
|
||||
this.$message({
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
||||
<ReportHeader top-title="营业收入" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange"
|
||||
/>
|
||||
@timeRangeChange="handleTimeChange" />
|
||||
<div class="main-body" style="
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@@ -16,8 +16,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<operatingSalesRevenue />
|
||||
<premProdStatus />
|
||||
<operatingSalesRevenue :saleData="saleData" />
|
||||
<premProdStatus :premiumProduct="premiumProduct" :salesProportion="salesProportion" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex; gap: 16px;margin-top: 6px;">
|
||||
@@ -26,7 +26,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<operatingLineChart />
|
||||
<operatingLineChart :salesTrendMap="salesTrendMap" :grossMarginTrendMap="grossMarginTrendMap" />
|
||||
<!-- <keyWork /> -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,12 +49,8 @@ import operatingSalesRevenue from "./components/operatingSalesRevenue.vue";
|
||||
import premProdStatus from "./components/premProdStatus.vue";
|
||||
import { mapState } from "vuex";
|
||||
import operatingLineChart from "./components/operatingLineChart";
|
||||
// import coreBottomLeft from "./components/coreBottomLeft.vue";
|
||||
// import orderProgress from "./components/orderProgress.vue";
|
||||
// import keyWork from "./components/keyWork.vue";
|
||||
import { getSalesRevenueData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
// import html2canvas from 'html2canvas'
|
||||
// import JsPDF from 'jspdf'
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -71,6 +67,11 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
saleData: {},
|
||||
premiumProduct: {},
|
||||
salesTrendMap: {},
|
||||
grossMarginTrendMap: {},
|
||||
salesProportion:{},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -128,14 +129,6 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
const startTime = moment().startOf("week").format("YYYY-MM-DD");
|
||||
const endTime = moment().format("YYYY-MM-DD");
|
||||
console.log(this.date, "date");
|
||||
this.date = [startTime, endTime];
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
this.loopTime();
|
||||
console.log(1111);
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -146,6 +139,24 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getData(obj) {
|
||||
getSalesRevenueData({
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
timeDim: obj.mode
|
||||
}).then((res) => {
|
||||
console.log(res);
|
||||
this.saleData = res.data.SaleData
|
||||
this.premiumProduct = res.data.premiumProduct
|
||||
this.salesTrendMap = res.data.salesTrendMap
|
||||
this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
||||
this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
||||
})
|
||||
},
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.getData(obj)
|
||||
},
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
|
||||
},
|
||||
@@ -153,28 +164,6 @@ export default {
|
||||
this.clientWidth = value;
|
||||
this.beilv2 = this.clientWidth / 1920;
|
||||
},
|
||||
getToday8StartTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 将时设为8、分0、秒0、毫秒0
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
// 获取第二天早上八点的时间戳
|
||||
getTomorrow8EndTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 先加一天,再设置为早上八点
|
||||
date.setDate(date.getDate() + 1);
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
loopTime() {
|
||||
const _this = this;
|
||||
_this.timer = setInterval(function () {
|
||||
// _this.getData()
|
||||
}, 60000);
|
||||
},
|
||||
change() {
|
||||
this.isFullScreen = screenfull.isFullscreen;
|
||||
},
|
||||
@@ -211,16 +200,6 @@ export default {
|
||||
}
|
||||
screenfull.toggle(this.$refs.dayReportB);
|
||||
},
|
||||
changeDate(val) {
|
||||
this.date = val;
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||
this.loopTime();
|
||||
} else {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
},
|
||||
// 导出
|
||||
// exportPDF() {
|
||||
// this.$message.success('正在导出,请稍等!')
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<profitLineChart :yName="'元/㎡'" :trendData="trendData" />
|
||||
<profitLineChart :yName="'元/㎡'" :trendData="trendData" :dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,9 +83,7 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
mode: undefined,
|
||||
dateData:{},
|
||||
levelId: undefined,
|
||||
itemData: [],
|
||||
trendData: [],
|
||||
@@ -172,9 +170,9 @@ export default {
|
||||
// startTime: this.startTime,
|
||||
// endTime: this.endTime,
|
||||
// mode: this.mode,
|
||||
startTime: 1762704000000,
|
||||
endTime: 1762790400000,
|
||||
mode: 1,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
trendName: "包装物辅材成本",
|
||||
levelId: this.levelId ? this.levelId : 1
|
||||
};
|
||||
@@ -192,16 +190,17 @@ export default {
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.startTime = obj.startTime
|
||||
this.endTime = obj.endTime
|
||||
this.mode = obj.mode
|
||||
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
selectChange(data) {
|
||||
console.log('选中的数据:', data);
|
||||
this.levelId = data
|
||||
if (this.startTime && this.endTime) {
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<profitLineChart :yName="'元/㎡'" :trendData="trendData" />
|
||||
<profitLineChart :yName="'元/㎡'" :trendData="trendData" :dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,9 +83,7 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
mode: undefined,
|
||||
dateData:{},
|
||||
levelId: undefined,
|
||||
itemData: [],
|
||||
trendData: [],
|
||||
@@ -172,9 +170,9 @@ export default {
|
||||
// startTime: this.startTime,
|
||||
// endTime: this.endTime,
|
||||
// mode: this.mode,
|
||||
startTime: 1762704000000,
|
||||
endTime: 1762790400000,
|
||||
mode: 1,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
trendName: "制造费用成本",
|
||||
levelId: this.levelId ? this.levelId : 1
|
||||
};
|
||||
@@ -192,16 +190,17 @@ export default {
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.startTime = obj.startTime
|
||||
this.endTime = obj.endTime
|
||||
this.mode = obj.mode
|
||||
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
selectChange(data) {
|
||||
console.log('选中的数据:', data);
|
||||
this.levelId = data
|
||||
if (this.startTime && this.endTime) {
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -158,9 +158,9 @@ export default {
|
||||
methods: {
|
||||
getData(obj) {
|
||||
getProfitAnalysisData({
|
||||
startTime: 1762704000000,
|
||||
endTime: 1762790400000,
|
||||
mode: 1
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}).then((res) => {
|
||||
|
||||
this.profitTotalData = res.data.overviewProfitData.filter(item => {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<div id="dayReport" class="dayReport" :style="styles">
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
||||
<ReportHeader size="psi" top-title="利润影响额分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange" />
|
||||
<ReportHeader size="psi" top-title="利润影响额分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange"
|
||||
@timeRangeChange="handleTimeChange" />
|
||||
<div class="main-body" style="
|
||||
margin-top: -20px;
|
||||
flex: 1;
|
||||
@@ -16,7 +17,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<singleTopSelect typeName="分析对象" />
|
||||
<singleTopSelect @query="handleGetData" :defaultType="'原料'" :typeList="typeList" typeName="分析对象" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex; gap: 16px;margin-top: 6px;">
|
||||
@@ -25,7 +26,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<totalProfit />
|
||||
<totalProfit :totalProfitIndict="totalProfitIndict" :dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex; gap: 16px;margin-top: 6px;">
|
||||
@@ -34,7 +35,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<profitLineChart :name=" '总利润趋势·元' " :yName="'元' " />
|
||||
<profitLineChart :trendData="totalProfitTrend" :dateData="dateData" :name=" '总利润趋势·元' " :yName="'元' " />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -52,20 +53,13 @@
|
||||
import ReportHeader from "./costComponents/single/noRouterHeader.vue";
|
||||
import { Sidebar } from "../../layout/components";
|
||||
import screenfull from "screenfull";
|
||||
import changeBase from "./costComponents/changeBase";
|
||||
// import changeBase from "./costComponents/changeBase";
|
||||
import totalProfit from "./costComponents/totalProfit.vue";
|
||||
import profitLineChart from "./costComponents/profitTotalChart.vue";
|
||||
import { mapState } from "vuex";
|
||||
import singleTopSelect from "./costComponents/singleTopSelect.vue";
|
||||
// import PSDO from "./components/PSDO.vue";
|
||||
// import psiLineChart from "./components/psiLineChart.vue";
|
||||
|
||||
// import coreBottomLeft from "./components/coreBottomLeft.vue";
|
||||
// import orderProgress from "./components/orderProgress.vue";
|
||||
// import keyWork from "./components/keyWork.vue";
|
||||
import moment from "moment";
|
||||
// import html2canvas from 'html2canvas'
|
||||
// import JsPDF from 'jspdf'
|
||||
import { getProfitImpactList } from '@/api/cockpit'
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -78,37 +72,19 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
weekArr: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
|
||||
isFullScreen: false,
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
coreProductVisualAlarmVO: [],
|
||||
defect: {},
|
||||
centerEqInfo: [
|
||||
{ title: "工单数量", num: 0 },
|
||||
{ title: "总产量/吨", num: 0 },
|
||||
{ title: "生产合格率", num: "0%" },
|
||||
{ title: "设备运行数量", num: 0 },
|
||||
{ title: "累计能耗/kwh", num: 0 },
|
||||
],
|
||||
outputTrend: {},
|
||||
coreProductVisualProcessVO: [],
|
||||
coreProductVisualWorkOrderVO: {},
|
||||
date: "",
|
||||
weekDay: "", // 导出报表用
|
||||
productNum: "",
|
||||
coreProductVisualLineVO: [],
|
||||
lineYieldVo: {}, // 产线良率
|
||||
productionOverviewVo: [], // 生产状况
|
||||
qualityDistributionVo: {}, // 本日不良分布
|
||||
equipmentUtilizationVo: {}, // 设备性能稼动率
|
||||
chipPowerDistributionVo: {}, // 芯片功率分布
|
||||
modulePowerDistributionVo: {}, // 组件功率分布
|
||||
chipPowerTrendVo: {},
|
||||
modulePowerTrendVo: {},
|
||||
equipmentProVo: [], // 表格
|
||||
equipmentStateVo: [], // 设备状态分布
|
||||
dateData: {},
|
||||
levelId: 1,
|
||||
analysisObject: [],
|
||||
typeList: [{
|
||||
name: '原料',
|
||||
id: 1,
|
||||
}],
|
||||
totalProfitIndict: [],
|
||||
totalProfitTrend:[],
|
||||
};
|
||||
},
|
||||
|
||||
@@ -166,14 +142,6 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
const startTime = moment().startOf("week").format("YYYY-MM-DD");
|
||||
const endTime = moment().format("YYYY-MM-DD");
|
||||
console.log(this.date, "date");
|
||||
this.date = [startTime, endTime];
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
this.loopTime();
|
||||
console.log(1111);
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -184,6 +152,49 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
// 基于选中的数据构建请求参数(根据实际接口需求调整)
|
||||
// let analysisObject = []
|
||||
|
||||
const requestParams = {
|
||||
// startTime: this.startTime,
|
||||
// endTime: this.endTime,
|
||||
// mode: this.mode,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
// analysisObject: this.analysisObject,
|
||||
analysisObject: ["石灰石"],
|
||||
levelId: this.levelId ? this.levelId : 1
|
||||
};
|
||||
// 调用接口
|
||||
getProfitImpactList(requestParams).then((res) => {
|
||||
this.totalProfitIndict = res.data.TotalProfitIndict
|
||||
this.totalProfitTrend = res.data.TotalProfitTrend
|
||||
});
|
||||
},
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
handleGetData(data) {
|
||||
console.log('从子组件接收的参数:', data);
|
||||
// params中包含type和levelId
|
||||
const { type, levelId } = data;
|
||||
this.analysisObject = []
|
||||
this.analysisObject.push(type)
|
||||
this.levelId = levelId
|
||||
// 在这里进行后续处理,如发起请求等
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
|
||||
},
|
||||
@@ -191,28 +202,6 @@ export default {
|
||||
this.clientWidth = value;
|
||||
this.beilv2 = this.clientWidth / 1920;
|
||||
},
|
||||
getToday8StartTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 将时设为8、分0、秒0、毫秒0
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
// 获取第二天早上八点的时间戳
|
||||
getTomorrow8EndTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 先加一天,再设置为早上八点
|
||||
date.setDate(date.getDate() + 1);
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
loopTime() {
|
||||
const _this = this;
|
||||
_this.timer = setInterval(function () {
|
||||
// _this.getData()
|
||||
}, 60000);
|
||||
},
|
||||
change() {
|
||||
this.isFullScreen = screenfull.isFullscreen;
|
||||
},
|
||||
@@ -249,16 +238,6 @@ export default {
|
||||
}
|
||||
screenfull.toggle(this.$refs.dayReportB);
|
||||
},
|
||||
changeDate(val) {
|
||||
this.date = val;
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||
this.loopTime();
|
||||
} else {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
},
|
||||
// 导出
|
||||
// exportPDF() {
|
||||
// this.$message.success('正在导出,请稍等!')
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<changeBase />
|
||||
<changeBase @selectChange="selectChange" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex; gap: 16px;margin-top: -20px;">
|
||||
@@ -35,7 +35,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<profitLineChart :yName="'元/㎡'" :dateData="dateData" />
|
||||
<profitLineChart :yName="'元/㎡'" :dateData="dateData" :trendData="trendData" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -152,14 +152,15 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getData(obj) {
|
||||
this.dateData = obj
|
||||
getData() {
|
||||
// this.dateData = obj
|
||||
// console.log('obj', obj);
|
||||
getCostAnalysisXXCostList({
|
||||
startTime: "1762704000290",
|
||||
endTime: "1762790399290",
|
||||
mode: 1,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
trendName: "原料成本",
|
||||
levelId: 2
|
||||
levelId: this.levelId
|
||||
}).then((res) => {
|
||||
this.itemData = res.data[0].map((item) => {
|
||||
return {
|
||||
@@ -172,7 +173,20 @@ export default {
|
||||
},
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.getData(obj)
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
|
||||
this.getData()
|
||||
},
|
||||
selectChange(data) {
|
||||
console.log('选中的数据:', data);
|
||||
this.levelId = data
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<div id="dayReport" class="dayReport" :style="styles">
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
||||
<ReportHeader size="psi" top-title="单项燃料分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange" />
|
||||
<ReportHeader size="psi" top-title="单项燃料分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange"
|
||||
@timeRangeChange="handleTimeChange" />
|
||||
<div class="main-body" style="
|
||||
margin-top: -20px;
|
||||
flex: 1;
|
||||
@@ -16,7 +17,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<singleTopSelect typeName="燃料" />
|
||||
<singleTopSelect :defaultType="'燃料成本'" :typeList="typeList" @query="handleGetData" typeName="燃料" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex; gap: 16px;margin-top: 8px;">
|
||||
@@ -25,8 +26,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<topLeftChart :name=" '采购单价·元/m³' " />
|
||||
<topRightChart :name="'消耗量·m³'" />
|
||||
<topLeftChart :dateData="dateData" :pupList="pupList" :name=" '采购单价·元/m³' " />
|
||||
<topRightChart :dateData="dateData" :consumptionList="consumptionList" :name="'消耗量·m³'" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,8 +37,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<bottomLeftChart :name="'单耗趋势·m³/㎡'" />
|
||||
<bottomLeftChart :name="'产量·㎡'" />
|
||||
<bottomLeftChart :dateData="dateData" :unitConsumptionList="unitConsumptionList" :name="'单耗趋势·m³/㎡'" />
|
||||
<bottomRightChart :dateData="dateData" :outputList="outputList" :name="'产量·㎡'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,6 +64,7 @@ import topLeftChart from "./costComponents/single/topLeftChart.vue";
|
||||
import topRightChart from "./costComponents/single/topRightChart.vue";
|
||||
import bottomLeftChart from "./costComponents/single/bottomLeftChart.vue";
|
||||
import bottomRightChart from "./costComponents/single/bottomRightChart.vue";
|
||||
import { getCostAnalysisXXCostList } from '@/api/cockpit'
|
||||
// import PSDO from "./components/PSDO.vue";
|
||||
// import psiLineChart from "./components/psiLineChart.vue";
|
||||
|
||||
@@ -93,32 +95,16 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
coreProductVisualAlarmVO: [],
|
||||
defect: {},
|
||||
centerEqInfo: [
|
||||
{ title: "工单数量", num: 0 },
|
||||
{ title: "总产量/吨", num: 0 },
|
||||
{ title: "生产合格率", num: "0%" },
|
||||
{ title: "设备运行数量", num: 0 },
|
||||
{ title: "累计能耗/kwh", num: 0 },
|
||||
],
|
||||
outputTrend: {},
|
||||
coreProductVisualProcessVO: [],
|
||||
coreProductVisualWorkOrderVO: {},
|
||||
date: "",
|
||||
weekDay: "", // 导出报表用
|
||||
productNum: "",
|
||||
coreProductVisualLineVO: [],
|
||||
lineYieldVo: {}, // 产线良率
|
||||
productionOverviewVo: [], // 生产状况
|
||||
qualityDistributionVo: {}, // 本日不良分布
|
||||
equipmentUtilizationVo: {}, // 设备性能稼动率
|
||||
chipPowerDistributionVo: {}, // 芯片功率分布
|
||||
modulePowerDistributionVo: {}, // 组件功率分布
|
||||
chipPowerTrendVo: {},
|
||||
modulePowerTrendVo: {},
|
||||
equipmentProVo: [], // 表格
|
||||
equipmentStateVo: [], // 设备状态分布
|
||||
pupList: [],
|
||||
consumptionList: [],
|
||||
unitConsumptionList: [],
|
||||
outputList: [],
|
||||
analysisObject: [],
|
||||
dateData: {},
|
||||
typeList: [{
|
||||
name: '燃料成本',
|
||||
id: 1,
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
@@ -176,14 +162,6 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
const startTime = moment().startOf("week").format("YYYY-MM-DD");
|
||||
const endTime = moment().format("YYYY-MM-DD");
|
||||
console.log(this.date, "date");
|
||||
this.date = [startTime, endTime];
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
this.loopTime();
|
||||
console.log(1111);
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -192,8 +170,57 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if (this.$route.query.name) {
|
||||
this.analysisObject = [this.$route.query.name]
|
||||
} else {
|
||||
this.analysisObject = ['燃料成本']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
// 基于选中的数据构建请求参数(根据实际接口需求调整)
|
||||
// let analysisObject = []
|
||||
|
||||
const requestParams = {
|
||||
// startTime: this.startTime,
|
||||
// endTime: this.endTime,
|
||||
// mode: this.mode,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
analysisObject: this.analysisObject,
|
||||
levelId: this.levelId ? this.levelId : 1
|
||||
};
|
||||
// 调用接口
|
||||
getCostAnalysisXXCostList(requestParams).then((res) => {
|
||||
this.pupList = res.data[0]
|
||||
this.consumptionList = res.data[1]
|
||||
this.unitConsumptionList = res.data[2]
|
||||
this.outputList = res.data[3]
|
||||
});
|
||||
},
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
handleGetData(data) {
|
||||
console.log('从子组件接收的参数:', data);
|
||||
// params中包含type和levelId
|
||||
const { type, levelId } = data;
|
||||
this.analysisObject = []
|
||||
this.analysisObject.push(type)
|
||||
this.levelId = levelId
|
||||
// 在这里进行后续处理,如发起请求等
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
|
||||
},
|
||||
@@ -201,28 +228,6 @@ export default {
|
||||
this.clientWidth = value;
|
||||
this.beilv2 = this.clientWidth / 1920;
|
||||
},
|
||||
getToday8StartTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 将时设为8、分0、秒0、毫秒0
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
// 获取第二天早上八点的时间戳
|
||||
getTomorrow8EndTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 先加一天,再设置为早上八点
|
||||
date.setDate(date.getDate() + 1);
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
loopTime() {
|
||||
const _this = this;
|
||||
_this.timer = setInterval(function () {
|
||||
// _this.getData()
|
||||
}, 60000);
|
||||
},
|
||||
change() {
|
||||
this.isFullScreen = screenfull.isFullscreen;
|
||||
},
|
||||
@@ -259,16 +264,6 @@ export default {
|
||||
}
|
||||
screenfull.toggle(this.$refs.dayReportB);
|
||||
},
|
||||
changeDate(val) {
|
||||
this.date = val;
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||
this.loopTime();
|
||||
} else {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
},
|
||||
// 导出
|
||||
// exportPDF() {
|
||||
// this.$message.success('正在导出,请稍等!')
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<div id="dayReport" class="dayReport" :style="styles">
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
||||
<ReportHeader size="psi" top-title="单项包装辅材分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange" />
|
||||
<ReportHeader size="psi" top-title="单项包装辅材分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange"
|
||||
@timeRangeChange="handleTimeChange" />
|
||||
<div class="main-body" style="
|
||||
margin-top: -20px;
|
||||
flex: 1;
|
||||
@@ -16,7 +17,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<singleTopSelect typeName="包装辅材" />
|
||||
<singleTopSelect :defaultType="'包装物辅材成本'" :typeList="typeList" @query="handleGetData" typeName="包装辅材" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex; gap: 16px;margin-top: 8px;">
|
||||
@@ -25,8 +26,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<topLeftChart :name=" '采购单价·元/只' " />
|
||||
<topRightChart :name="'消耗量·只'" />
|
||||
<topLeftChart :dateData="dateData" :pupList="pupList" :name=" '采购单价·元/只' " />
|
||||
<topRightChart :dateData="dateData" :consumptionList="consumptionList" :name="'消耗量·只'" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,8 +37,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<bottomLeftChart :name="'单耗趋势·只/㎡'" />
|
||||
<bottomLeftChart :name="'产量·㎡'" />
|
||||
<bottomLeftChart :dateData="dateData" :unitConsumptionList="unitConsumptionList" :name="'单耗趋势·只/㎡'" />
|
||||
<bottomRightChart :dateData="dateData" :outputList="outputList" :name="'产量·㎡'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,6 +65,7 @@ import topLeftChart from "./costComponents/single/topLeftChart.vue";
|
||||
import topRightChart from "./costComponents/single/topRightChart.vue";
|
||||
import bottomLeftChart from "./costComponents/single/bottomLeftChart.vue";
|
||||
import bottomRightChart from "./costComponents/single/bottomRightChart.vue";
|
||||
import { getCostAnalysisXXCostList } from '@/api/cockpit'
|
||||
// import PSDO from "./components/PSDO.vue";
|
||||
// import psiLineChart from "./components/psiLineChart.vue";
|
||||
|
||||
@@ -94,32 +96,16 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
coreProductVisualAlarmVO: [],
|
||||
defect: {},
|
||||
centerEqInfo: [
|
||||
{ title: "工单数量", num: 0 },
|
||||
{ title: "总产量/吨", num: 0 },
|
||||
{ title: "生产合格率", num: "0%" },
|
||||
{ title: "设备运行数量", num: 0 },
|
||||
{ title: "累计能耗/kwh", num: 0 },
|
||||
],
|
||||
outputTrend: {},
|
||||
coreProductVisualProcessVO: [],
|
||||
coreProductVisualWorkOrderVO: {},
|
||||
date: "",
|
||||
weekDay: "", // 导出报表用
|
||||
productNum: "",
|
||||
coreProductVisualLineVO: [],
|
||||
lineYieldVo: {}, // 产线良率
|
||||
productionOverviewVo: [], // 生产状况
|
||||
qualityDistributionVo: {}, // 本日不良分布
|
||||
equipmentUtilizationVo: {}, // 设备性能稼动率
|
||||
chipPowerDistributionVo: {}, // 芯片功率分布
|
||||
modulePowerDistributionVo: {}, // 组件功率分布
|
||||
chipPowerTrendVo: {},
|
||||
modulePowerTrendVo: {},
|
||||
equipmentProVo: [], // 表格
|
||||
equipmentStateVo: [], // 设备状态分布
|
||||
pupList: [],
|
||||
consumptionList: [],
|
||||
unitConsumptionList: [],
|
||||
outputList: [],
|
||||
dateData:{},
|
||||
analysisObject: [],
|
||||
typeList: [{
|
||||
name: '包装物辅材成本',
|
||||
id: 1,
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
@@ -177,14 +163,6 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
const startTime = moment().startOf("week").format("YYYY-MM-DD");
|
||||
const endTime = moment().format("YYYY-MM-DD");
|
||||
console.log(this.date, "date");
|
||||
this.date = [startTime, endTime];
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
this.loopTime();
|
||||
console.log(1111);
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -193,8 +171,57 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if (this.$route.query.name) {
|
||||
this.analysisObject = [this.$route.query.name]
|
||||
} else {
|
||||
this.analysisObject = ['包装物辅材成本']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
// 基于选中的数据构建请求参数(根据实际接口需求调整)
|
||||
// let analysisObject = []
|
||||
|
||||
const requestParams = {
|
||||
// startTime: this.startTime,
|
||||
// endTime: this.endTime,
|
||||
// mode: this.mode,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
analysisObject: this.analysisObject,
|
||||
levelId: this.levelId ? this.levelId : 1
|
||||
};
|
||||
// 调用接口
|
||||
getCostAnalysisXXCostList(requestParams).then((res) => {
|
||||
this.pupList = res.data[0]
|
||||
this.consumptionList = res.data[1]
|
||||
this.unitConsumptionList = res.data[2]
|
||||
this.outputList = res.data[3]
|
||||
});
|
||||
},
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
handleGetData(data) {
|
||||
console.log('从子组件接收的参数:', data);
|
||||
// params中包含type和levelId
|
||||
const { type, levelId } = data;
|
||||
this.analysisObject = []
|
||||
this.analysisObject.push(type)
|
||||
this.levelId = levelId
|
||||
// 在这里进行后续处理,如发起请求等
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
|
||||
},
|
||||
@@ -202,28 +229,6 @@ export default {
|
||||
this.clientWidth = value;
|
||||
this.beilv2 = this.clientWidth / 1920;
|
||||
},
|
||||
getToday8StartTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 将时设为8、分0、秒0、毫秒0
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
// 获取第二天早上八点的时间戳
|
||||
getTomorrow8EndTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 先加一天,再设置为早上八点
|
||||
date.setDate(date.getDate() + 1);
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
loopTime() {
|
||||
const _this = this;
|
||||
_this.timer = setInterval(function () {
|
||||
// _this.getData()
|
||||
}, 60000);
|
||||
},
|
||||
change() {
|
||||
this.isFullScreen = screenfull.isFullscreen;
|
||||
},
|
||||
@@ -260,16 +265,6 @@ export default {
|
||||
}
|
||||
screenfull.toggle(this.$refs.dayReportB);
|
||||
},
|
||||
changeDate(val) {
|
||||
this.date = val;
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||
this.loopTime();
|
||||
} else {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
},
|
||||
// 导出
|
||||
// exportPDF() {
|
||||
// this.$message.success('正在导出,请稍等!')
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<div id="dayReport" class="dayReport" :style="styles">
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
||||
<ReportHeader size="psi" top-title="单项原料分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange" />
|
||||
<ReportHeader size="psi" top-title="单项原料分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange"
|
||||
@timeRangeChange="handleTimeChange" />
|
||||
<div class="main-body" style="
|
||||
margin-top: -20px;
|
||||
flex: 1;
|
||||
@@ -16,7 +17,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<singleTopSelect typeName="原料" />
|
||||
<singleTopSelect typeName="原料" @query="handleGetData" :defaultType="'原料成本'" :typeList="rawTypeList" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex; gap: 16px;margin-top:8px;">
|
||||
@@ -25,8 +26,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<topLeftChart :name=" '采购单价·元/吨' " />
|
||||
<topRightChart :name="'消耗量·吨'" />
|
||||
<topLeftChart :dateData="dateData" :pupList="pupList" :name=" '采购单价·元/吨' " />
|
||||
<topRightChart :dateData="dateData" :consumptionList="consumptionList" :name="'消耗量·吨'" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,8 +37,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<bottomLeftChart :name="'单耗趋势·吨/㎡'" />
|
||||
<bottomLeftChart :name="'产量·㎡'" />
|
||||
<bottomLeftChart :dateData="dateData" :unitConsumptionList="unitConsumptionList" :name="'单耗趋势·吨/㎡'" />
|
||||
<bottomRightChart :dateData="dateData" :outputList="outputList" :name="'产量·㎡'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,12 +64,7 @@ import topLeftChart from "./costComponents/single/topLeftChart.vue";
|
||||
import topRightChart from "./costComponents/single/topRightChart.vue";
|
||||
import bottomLeftChart from "./costComponents/single/bottomLeftChart.vue";
|
||||
import bottomRightChart from "./costComponents/single/bottomRightChart.vue";
|
||||
// import PSDO from "./components/PSDO.vue";
|
||||
// import psiLineChart from "./components/psiLineChart.vue";
|
||||
|
||||
// import coreBottomLeft from "./components/coreBottomLeft.vue";
|
||||
// import orderProgress from "./components/orderProgress.vue";
|
||||
// import keyWork from "./components/keyWork.vue";
|
||||
import { getCostAnalysisXXCostList } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
// import html2canvas from 'html2canvas'
|
||||
// import JsPDF from 'jspdf'
|
||||
@@ -88,37 +84,22 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
weekArr: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
|
||||
isFullScreen: false,
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
coreProductVisualAlarmVO: [],
|
||||
defect: {},
|
||||
centerEqInfo: [
|
||||
{ title: "工单数量", num: 0 },
|
||||
{ title: "总产量/吨", num: 0 },
|
||||
{ title: "生产合格率", num: "0%" },
|
||||
{ title: "设备运行数量", num: 0 },
|
||||
{ title: "累计能耗/kwh", num: 0 },
|
||||
],
|
||||
outputTrend: {},
|
||||
coreProductVisualProcessVO: [],
|
||||
coreProductVisualWorkOrderVO: {},
|
||||
date: "",
|
||||
weekDay: "", // 导出报表用
|
||||
productNum: "",
|
||||
coreProductVisualLineVO: [],
|
||||
lineYieldVo: {}, // 产线良率
|
||||
productionOverviewVo: [], // 生产状况
|
||||
qualityDistributionVo: {}, // 本日不良分布
|
||||
equipmentUtilizationVo: {}, // 设备性能稼动率
|
||||
chipPowerDistributionVo: {}, // 芯片功率分布
|
||||
modulePowerDistributionVo: {}, // 组件功率分布
|
||||
chipPowerTrendVo: {},
|
||||
modulePowerTrendVo: {},
|
||||
equipmentProVo: [], // 表格
|
||||
equipmentStateVo: [], // 设备状态分布
|
||||
dateData: {},
|
||||
levelId: 1,
|
||||
pupList: [],
|
||||
consumptionList: [],
|
||||
dateData: {},
|
||||
unitConsumptionList: [],
|
||||
outputList: [],
|
||||
analysisObject: [],
|
||||
rawTypeList: [{
|
||||
name: '原料成本',
|
||||
id: 1,
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
@@ -176,14 +157,6 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
const startTime = moment().startOf("week").format("YYYY-MM-DD");
|
||||
const endTime = moment().format("YYYY-MM-DD");
|
||||
console.log(this.date, "date");
|
||||
this.date = [startTime, endTime];
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
this.loopTime();
|
||||
console.log(1111);
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -192,8 +165,57 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if (this.$route.query.name) {
|
||||
this.analysisObject = [this.$route.query.name]
|
||||
} else {
|
||||
this.analysisObject = ['原料成本']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
// 基于选中的数据构建请求参数(根据实际接口需求调整)
|
||||
// let analysisObject = []
|
||||
|
||||
const requestParams = {
|
||||
// startTime: this.startTime,
|
||||
// endTime: this.endTime,
|
||||
// mode: this.mode,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
analysisObject: this.analysisObject,
|
||||
levelId: this.levelId ? this.levelId : 1
|
||||
};
|
||||
// 调用接口
|
||||
getCostAnalysisXXCostList(requestParams).then((res) => {
|
||||
this.pupList = res.data[0]
|
||||
this.consumptionList = res.data[1]
|
||||
this.unitConsumptionList = res.data[2]
|
||||
this.outputList = res.data[3]
|
||||
});
|
||||
},
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
handleGetData(data) {
|
||||
console.log('从子组件接收的参数:', data);
|
||||
// params中包含type和levelId
|
||||
const { type, levelId } = data;
|
||||
this.analysisObject = []
|
||||
this.analysisObject.push(type)
|
||||
this.levelId = levelId
|
||||
// 在这里进行后续处理,如发起请求等
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
|
||||
},
|
||||
@@ -201,28 +223,6 @@ export default {
|
||||
this.clientWidth = value;
|
||||
this.beilv2 = this.clientWidth / 1920;
|
||||
},
|
||||
getToday8StartTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 将时设为8、分0、秒0、毫秒0
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
// 获取第二天早上八点的时间戳
|
||||
getTomorrow8EndTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 先加一天,再设置为早上八点
|
||||
date.setDate(date.getDate() + 1);
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
loopTime() {
|
||||
const _this = this;
|
||||
_this.timer = setInterval(function () {
|
||||
// _this.getData()
|
||||
}, 60000);
|
||||
},
|
||||
change() {
|
||||
this.isFullScreen = screenfull.isFullscreen;
|
||||
},
|
||||
@@ -259,16 +259,6 @@ export default {
|
||||
}
|
||||
screenfull.toggle(this.$refs.dayReportB);
|
||||
},
|
||||
changeDate(val) {
|
||||
this.date = val;
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||
this.loopTime();
|
||||
} else {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
},
|
||||
// 导出
|
||||
// exportPDF() {
|
||||
// this.$message.success('正在导出,请稍等!')
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<div id="dayReport" class="dayReport" :style="styles">
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
||||
<ReportHeader size="psi" top-title="单项燃动力分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange" />
|
||||
<ReportHeader size="psi" top-title="单项燃动力分析" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange"
|
||||
@timeRangeChange="handleTimeChange" />
|
||||
<div class="main-body" style="
|
||||
margin-top: -20px;
|
||||
flex: 1;
|
||||
@@ -16,7 +17,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<singleTopSelect typeName="燃动力" />
|
||||
<singleTopSelect :defaultType="'燃动力成本'" :typeList="typeList" @query="handleGetData" typeName="燃动力" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex; gap: 16px;margin-top: 8px;">
|
||||
@@ -25,8 +26,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<topLeftChart :name=" '采购单价·元/吨' " />
|
||||
<topRightChart :name="'消耗量·吨'" />
|
||||
<topLeftChart :dateData="dateData" :pupList="pupList" :name=" '采购单价·元/吨' " />
|
||||
<topRightChart :dateData="dateData" :consumptionList="consumptionList" :name="'消耗量·吨'" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,8 +37,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<bottomLeftChart :name="'单耗趋势·吨/㎡'" />
|
||||
<bottomLeftChart :name="'产量·㎡'" />
|
||||
<bottomLeftChart :dateData="dateData" :unitConsumptionList="unitConsumptionList" :name="'单耗趋势·吨/㎡'" />
|
||||
<bottomRightChart :dateData="dateData" :outputList="outputList" :name="'产量·㎡'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,6 +64,7 @@ import topLeftChart from "./costComponents/single/topLeftChart.vue";
|
||||
import topRightChart from "./costComponents/single/topRightChart.vue";
|
||||
import bottomLeftChart from "./costComponents/single/bottomLeftChart.vue";
|
||||
import bottomRightChart from "./costComponents/single/bottomRightChart.vue";
|
||||
import { getCostAnalysisXXCostList } from '@/api/cockpit'
|
||||
// import PSDO from "./components/PSDO.vue";
|
||||
// import psiLineChart from "./components/psiLineChart.vue";
|
||||
|
||||
@@ -88,37 +90,22 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
weekArr: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
|
||||
isFullScreen: false,
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
coreProductVisualAlarmVO: [],
|
||||
defect: {},
|
||||
centerEqInfo: [
|
||||
{ title: "工单数量", num: 0 },
|
||||
{ title: "总产量/吨", num: 0 },
|
||||
{ title: "生产合格率", num: "0%" },
|
||||
{ title: "设备运行数量", num: 0 },
|
||||
{ title: "累计能耗/kwh", num: 0 },
|
||||
],
|
||||
outputTrend: {},
|
||||
coreProductVisualProcessVO: [],
|
||||
coreProductVisualWorkOrderVO: {},
|
||||
date: "",
|
||||
weekDay: "", // 导出报表用
|
||||
productNum: "",
|
||||
coreProductVisualLineVO: [],
|
||||
lineYieldVo: {}, // 产线良率
|
||||
productionOverviewVo: [], // 生产状况
|
||||
qualityDistributionVo: {}, // 本日不良分布
|
||||
equipmentUtilizationVo: {}, // 设备性能稼动率
|
||||
chipPowerDistributionVo: {}, // 芯片功率分布
|
||||
modulePowerDistributionVo: {}, // 组件功率分布
|
||||
chipPowerTrendVo: {},
|
||||
modulePowerTrendVo: {},
|
||||
equipmentProVo: [], // 表格
|
||||
equipmentStateVo: [], // 设备状态分布
|
||||
dateData: {},
|
||||
levelId: 1,
|
||||
pupList: [],
|
||||
consumptionList: [],
|
||||
unitConsumptionList: [],
|
||||
outputList: [],
|
||||
dateData: {},
|
||||
analysisObject: [],
|
||||
typeList: [{
|
||||
name: '燃动力成本',
|
||||
id: 1,
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
@@ -176,14 +163,6 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
const startTime = moment().startOf("week").format("YYYY-MM-DD");
|
||||
const endTime = moment().format("YYYY-MM-DD");
|
||||
console.log(this.date, "date");
|
||||
this.date = [startTime, endTime];
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
this.loopTime();
|
||||
console.log(1111);
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -192,8 +171,57 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if (this.$route.query.name) {
|
||||
this.analysisObject = [this.$route.query.name]
|
||||
} else {
|
||||
this.analysisObject = ['燃动力成本']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
// 基于选中的数据构建请求参数(根据实际接口需求调整)
|
||||
// let analysisObject = []
|
||||
|
||||
const requestParams = {
|
||||
// startTime: this.startTime,
|
||||
// endTime: this.endTime,
|
||||
// mode: this.mode,
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
mode: this.dateData.mode,
|
||||
analysisObject: this.analysisObject,
|
||||
levelId: this.levelId ? this.levelId : 1
|
||||
};
|
||||
// 调用接口
|
||||
getCostAnalysisXXCostList(requestParams).then((res) => {
|
||||
this.pupList = res.data[0]
|
||||
this.consumptionList = res.data[1]
|
||||
this.unitConsumptionList = res.data[2]
|
||||
this.outputList = res.data[3]
|
||||
});
|
||||
},
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log(obj, 'obj');
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
mode: obj.mode,
|
||||
}
|
||||
this.getData()
|
||||
},
|
||||
handleGetData(data) {
|
||||
console.log('从子组件接收的参数:', data);
|
||||
// params中包含type和levelId
|
||||
const { type, levelId } = data;
|
||||
this.analysisObject = []
|
||||
this.analysisObject.push(type)
|
||||
this.levelId = levelId
|
||||
// 在这里进行后续处理,如发起请求等
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
|
||||
},
|
||||
@@ -201,28 +229,6 @@ export default {
|
||||
this.clientWidth = value;
|
||||
this.beilv2 = this.clientWidth / 1920;
|
||||
},
|
||||
getToday8StartTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 将时设为8、分0、秒0、毫秒0
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
// 获取第二天早上八点的时间戳
|
||||
getTomorrow8EndTimestamp() {
|
||||
const date = new Date(); // 获取当前时间
|
||||
// 先加一天,再设置为早上八点
|
||||
date.setDate(date.getDate() + 1);
|
||||
date.setHours(8, 0, 0, 0);
|
||||
return date.getTime(); // 转换为时间戳(毫秒)
|
||||
},
|
||||
|
||||
loopTime() {
|
||||
const _this = this;
|
||||
_this.timer = setInterval(function () {
|
||||
// _this.getData()
|
||||
}, 60000);
|
||||
},
|
||||
change() {
|
||||
this.isFullScreen = screenfull.isFullscreen;
|
||||
},
|
||||
@@ -259,16 +265,6 @@ export default {
|
||||
}
|
||||
screenfull.toggle(this.$refs.dayReportB);
|
||||
},
|
||||
changeDate(val) {
|
||||
this.date = val;
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||
this.loopTime();
|
||||
} else {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
},
|
||||
// 导出
|
||||
// exportPDF() {
|
||||
// this.$message.success('正在导出,请稍等!')
|
||||
|
||||
159
vue.config.js
159
vue.config.js
@@ -1,16 +1,16 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
const defaultSettings = require('./src/settings.js')
|
||||
"use strict";
|
||||
const path = require("path");
|
||||
const defaultSettings = require("./src/settings.js");
|
||||
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, dir)
|
||||
return path.join(__dirname, dir);
|
||||
}
|
||||
|
||||
const CompressionPlugin = require('compression-webpack-plugin')
|
||||
const CompressionPlugin = require("compression-webpack-plugin");
|
||||
|
||||
const name = process.env.VUE_APP_TITLE || "洛玻集团驾驶舱"; // 网页标题
|
||||
|
||||
const port = process.env.port || process.env.npm_config_port || 80 // 端口
|
||||
const port = process.env.port || process.env.npm_config_port || 80; // 端口
|
||||
|
||||
// vue.config.js 配置说明
|
||||
//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
|
||||
@@ -19,120 +19,115 @@ module.exports = {
|
||||
// 部署生产环境和开发环境下的URL。
|
||||
// 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
|
||||
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
|
||||
publicPath: process.env.PUBLIC_PATH ? process.env.PUBLIC_PATH : '/',
|
||||
publicPath: process.env.PUBLIC_PATH ? process.env.PUBLIC_PATH : "/",
|
||||
// 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
|
||||
outputDir: 'dist',
|
||||
outputDir: "dist",
|
||||
// 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
|
||||
assetsDir: 'static',
|
||||
assetsDir: "static",
|
||||
// 是否开启eslint保存检测,有效值:ture | false | 'error'
|
||||
lintOnSave: process.env.NODE_ENV === 'development',
|
||||
lintOnSave: process.env.NODE_ENV === "development",
|
||||
// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
|
||||
productionSourceMap: false,
|
||||
// webpack-dev-server 相关配置
|
||||
devServer: {
|
||||
host: '0.0.0.0',
|
||||
host: "0.0.0.0",
|
||||
port: port,
|
||||
open: true,
|
||||
proxy: {
|
||||
// detail: https://cli.vuejs.org/config/#devserver-proxy
|
||||
['/proxy-api']: {
|
||||
["/proxy-api"]: {
|
||||
target: `http://localhost:48080`,
|
||||
// target: `http://api-dashboard.yudao.iocoder.cn`,
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
['^' + process.env.VUE_APP_BASE_API]: ''
|
||||
}
|
||||
}
|
||||
["^" + process.env.VUE_APP_BASE_API]: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
disableHostCheck: true
|
||||
disableHostCheck: true,
|
||||
},
|
||||
css: {
|
||||
loaderOptions: {
|
||||
sass: {
|
||||
sassOptions: { outputStyle: "expanded" }
|
||||
}
|
||||
}
|
||||
sassOptions: { outputStyle: "expanded" },
|
||||
},
|
||||
},
|
||||
},
|
||||
configureWebpack: {
|
||||
name: name,
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src')
|
||||
}
|
||||
"@": resolve("src"),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
|
||||
new CompressionPlugin({
|
||||
cache: false, // 不启用文件缓存
|
||||
test: /\.(js|css|html)?$/i, // 压缩文件格式
|
||||
filename: '[path].gz[query]', // 压缩后的文件名
|
||||
algorithm: 'gzip', // 使用gzip压缩
|
||||
minRatio: 0.8 // 压缩率小于1才会压缩
|
||||
})
|
||||
cache: false, // 不启用文件缓存
|
||||
test: /\.(js|css|html)?$/i, // 压缩文件格式
|
||||
filename: "[path].gz[query]", // 压缩后的文件名
|
||||
algorithm: "gzip", // 使用gzip压缩
|
||||
minRatio: 0.8, // 压缩率小于1才会压缩
|
||||
}),
|
||||
],
|
||||
},
|
||||
chainWebpack(config) {
|
||||
config.plugins.delete('preload') // TODO: need test
|
||||
config.plugins.delete('prefetch') // TODO: need test
|
||||
config.plugins.delete("preload"); // TODO: need test
|
||||
config.plugins.delete("prefetch"); // TODO: need test
|
||||
|
||||
// set svg-sprite-loader
|
||||
config.module.rule("svg").exclude.add(resolve("src/assets/icons")).end();
|
||||
config.module
|
||||
.rule('svg')
|
||||
.exclude.add(resolve('src/assets/icons'))
|
||||
.end()
|
||||
config.module
|
||||
.rule('icons')
|
||||
.rule("icons")
|
||||
.test(/\.svg$/)
|
||||
.include.add(resolve('src/assets/icons'))
|
||||
.include.add(resolve("src/assets/icons"))
|
||||
.end()
|
||||
.use('svg-sprite-loader')
|
||||
.loader('svg-sprite-loader')
|
||||
.use("svg-sprite-loader")
|
||||
.loader("svg-sprite-loader")
|
||||
.options({
|
||||
symbolId: 'icon-[name]'
|
||||
symbolId: "icon-[name]",
|
||||
})
|
||||
.end()
|
||||
.end();
|
||||
|
||||
config
|
||||
.when(process.env.NODE_ENV !== 'development',
|
||||
config => {
|
||||
config
|
||||
.plugin('ScriptExtHtmlWebpackPlugin')
|
||||
.after('html')
|
||||
.use('script-ext-html-webpack-plugin', [{
|
||||
// `runtime` must same as runtimeChunk name. default is `runtime`
|
||||
inline: /runtime\..*\.js$/
|
||||
}])
|
||||
.end()
|
||||
config
|
||||
.optimization.splitChunks({
|
||||
chunks: 'all',
|
||||
cacheGroups: {
|
||||
libs: {
|
||||
name: 'chunk-libs',
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
priority: 10,
|
||||
chunks: 'initial' // only package third parties that are initially dependent
|
||||
},
|
||||
elementUI: {
|
||||
name: 'chunk-elementUI', // split elementUI into a single package
|
||||
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
|
||||
test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
|
||||
},
|
||||
commons: {
|
||||
name: 'chunk-commons',
|
||||
test: resolve('src/components'), // can customize your rules
|
||||
minChunks: 3, // minimum common number
|
||||
priority: 5,
|
||||
reuseExistingChunk: true
|
||||
}
|
||||
}
|
||||
})
|
||||
config.optimization.runtimeChunk('single'),
|
||||
config.when(process.env.NODE_ENV !== "development", (config) => {
|
||||
config
|
||||
.plugin("ScriptExtHtmlWebpackPlugin")
|
||||
.after("html")
|
||||
.use("script-ext-html-webpack-plugin", [
|
||||
{
|
||||
from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件
|
||||
to: './', //到根目录下
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
// `runtime` must same as runtimeChunk name. default is `runtime`
|
||||
inline: /runtime\..*\.js$/,
|
||||
},
|
||||
])
|
||||
.end();
|
||||
config.optimization.splitChunks({
|
||||
chunks: "all",
|
||||
cacheGroups: {
|
||||
libs: {
|
||||
name: "chunk-libs",
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
priority: 10,
|
||||
chunks: "initial", // only package third parties that are initially dependent
|
||||
},
|
||||
elementUI: {
|
||||
name: "chunk-elementUI", // split elementUI into a single package
|
||||
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
|
||||
test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
|
||||
},
|
||||
commons: {
|
||||
name: "chunk-commons",
|
||||
test: resolve("src/components"), // can customize your rules
|
||||
minChunks: 3, // minimum common number
|
||||
priority: 5,
|
||||
reuseExistingChunk: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
config.optimization.runtimeChunk("single"),
|
||||
{
|
||||
from: path.resolve(__dirname, "./public/robots.txt"), //防爬虫文件
|
||||
to: "./", //到根目录下
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user