修改
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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轴)
|
||||
// {
|
||||
|
||||
Reference in New Issue
Block a user