修改
This commit is contained in:
@@ -70,7 +70,7 @@ export default {
|
||||
label: { backgroundColor: '#6a7985' }
|
||||
}
|
||||
},
|
||||
grid: { top: 10, bottom: 20, right: 25, left: 30 },
|
||||
grid: { top: 10, bottom: 20, right: 25, left: 50 },
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
@@ -94,9 +94,9 @@ export default {
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
nameTextStyle: { color: 'rgba(0, 0, 0, 0.45)', fontSize: 14, align: 'left' },
|
||||
min: () => 0,
|
||||
max: (value) => Math.ceil(value.max),
|
||||
scale: true,
|
||||
// min: () => 0,
|
||||
// max: (value) => Math.ceil(value.max),
|
||||
// scale: true,
|
||||
axisTick: { show: false },
|
||||
axisLabel: { color: 'rgba(0, 0, 0, 0.45)', fontSize: 12 },
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="right">
|
||||
<!-- 完成率颜色动态绑定 -->
|
||||
<div class="number" :style="{ 'color': item.completed === 0 ? '#FF8400' : 'rgba(54, 181, 138, .7)' }">
|
||||
{{ item.proportion !== null && item.proportion !== undefined ? (item.proportion * 100) + '%' : '0%' }}
|
||||
{{ item.proportion !== null && item.proportion !== undefined ? (item.proportion) + '%' : '0%' }}
|
||||
</div>
|
||||
<div class="title">完成率</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="right">
|
||||
<!-- 完成率颜色动态绑定 -->
|
||||
<div class="number" :style="{ 'color': item.completed === 0 ? '#FF8400' : 'rgba(54, 181, 138, .7)' }">
|
||||
{{ item.proportion !== null && item.proportion !== undefined ? (item.proportion * 100) + '%' : '0%' }}
|
||||
{{ item.proportion !== null && item.proportion !== undefined ? (item.proportion) + '%' : '0%' }}
|
||||
</div>
|
||||
<div class="title">完成率</div>
|
||||
</div>
|
||||
|
||||
@@ -80,7 +80,7 @@ export default {
|
||||
return {
|
||||
targetValue: targetItem?.[`${field}Target`] || 0, // 对应指标的目标值字段(如 profitTarget)
|
||||
value: targetItem?.[field] || 0, // 对应指标的实际值
|
||||
proportion: (targetItem?.[`${field}Proportion`] || 0) * 100, // 对应指标的占比(转百分比)
|
||||
proportion: (targetItem?.[`${field}Proportion`] || 0), // 对应指标的占比(转百分比)
|
||||
completed: targetItem?.[`${field}Completed`] ?? 0 // 状态字段(复用分公司的 completed)
|
||||
};
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="content-wrapper">
|
||||
<div class="left">
|
||||
<div class="number" style="color: rgba(103, 103, 103, 0.79);">{{ item.targetValue }}</div>
|
||||
<div class="title">目标值</div>
|
||||
<div class="title" style="color: rgba(134, 134, 135, 1);">目标值</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<!-- 实际值:根据 实际值≥目标值 动态绑定类名 -->
|
||||
@@ -17,7 +17,7 @@
|
||||
}">
|
||||
{{ item.currentValue }}
|
||||
</div>
|
||||
<div class="title">实际值</div>
|
||||
<div class="title" style="color: rgba(134, 134, 135, 1);">实际值</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
@@ -236,7 +236,7 @@ export default {
|
||||
|
||||
/* 百分比 - 实际值≥目标值(绿色) */
|
||||
.percent-exceed {
|
||||
color: #28CB97 !important;
|
||||
color: rgba(54, 181, 138, 1) !important;
|
||||
}
|
||||
|
||||
/* 百分比 - 实际值<目标值(黄色) */
|
||||
|
||||
@@ -55,14 +55,14 @@ export default {
|
||||
unit: "原片成本·元/㎡",
|
||||
route: "cost/cost",
|
||||
target: 16,
|
||||
actual: 18,
|
||||
actual: 16,
|
||||
progress: 110
|
||||
},
|
||||
{
|
||||
unit: "加工成本·元/㎡",
|
||||
route: "cost/cost",
|
||||
target: 16,
|
||||
actual: 14,
|
||||
actual: 16,
|
||||
progress: 85
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
<template>
|
||||
<div class="coreItem" :style="{ 'height': height + 'px' }">
|
||||
<!-- 用 v-for 动态生成每个 item -->
|
||||
<!-- 遍历每个item,使用item自身的flag -->
|
||||
<div class="item" v-for="(item, index) in itemList" :key="index">
|
||||
<div class="unit">{{ item.name }}</div>
|
||||
<div class="item-content">
|
||||
<!-- 左右内容容器 -->
|
||||
<div class="content-wrapper">
|
||||
<div class="left">
|
||||
<div class="number"> {{ item.targetValue || 0 }}</div>
|
||||
<div class="title">目标值</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<!-- 实际值:根据 flag 动态绑定颜色 -->
|
||||
<!-- 实际值:使用当前item的flag判断颜色 -->
|
||||
<div class="right">
|
||||
<div class="number" :style="{ 'color': flagColor }"> {{ item.value || 0 }}</div>
|
||||
<div class="number"
|
||||
:style="{ 'color': item.flag === 0 ? 'rgba(255, 132, 0, 1)' : 'rgba(54, 181, 138, 1)' }">
|
||||
{{ item.value || 0 }}
|
||||
</div>
|
||||
<div class="title">实际值</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<!-- 完成率:根据 flag 动态绑定颜色 -->
|
||||
<!-- 完成率:同样使用当前item的flag判断颜色 -->
|
||||
<div class="right">
|
||||
<div class="number" :style="{ 'color': flagColor }"> {{ item.proportion !== null && item.proportion !== undefined ? (item.proportion) + '%' : '0%' }}
|
||||
<div class="number"
|
||||
:style="{ 'color': item.flag === 0 ? 'rgba(255, 132, 0, 1)' : 'rgba(54, 181, 138, 1)' }">
|
||||
{{ item.proportion != null ? (item.proportion + '%') : '0%' }}
|
||||
</div>
|
||||
<div class="title">完成率</div>
|
||||
</div>
|
||||
@@ -35,23 +39,15 @@ export default {
|
||||
components: {},
|
||||
props: [
|
||||
'itemList',
|
||||
'height',
|
||||
'flag' // 新增 flag 属性,用于控制颜色
|
||||
'height'
|
||||
// 移除组件级的flag属性,不再需要
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
progress: 90,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 根据 flag 计算实际值和完成率的颜色
|
||||
flagColor() {
|
||||
// flag 为 0 时使用灰色,为 1 时使用橙色
|
||||
return this.flag === 1
|
||||
? 'rgba(255, 132, 0, 1)'
|
||||
: 'rgba(54, 181, 138, 1)';
|
||||
}
|
||||
},
|
||||
// 移除flagColor计算属性,改为item级判断
|
||||
watch: {
|
||||
itemList: {
|
||||
handler(newList) {
|
||||
@@ -65,7 +61,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 原有样式保持不变 */
|
||||
/* 样式保持不变 */
|
||||
.coreItem {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
@@ -143,7 +139,6 @@ export default {
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
/* 颜色由动态样式绑定控制,此处不设置默认值 */
|
||||
}
|
||||
|
||||
.title {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
||||
<div class="topItem-container" style="display: flex; flex-direction: column;gap: 16px;overflow: hidden;">
|
||||
<!-- <topItem :itemList="parentItemList" /> -->
|
||||
<rawItem :itemList="itemData" />
|
||||
<rawItem :itemList="sortedItemData" />
|
||||
|
||||
</div>
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
@@ -50,6 +50,21 @@ export default {
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
||||
sortedItemData() {
|
||||
// 过滤出包含“总成本”的项(不区分大小写)
|
||||
const totalCostItems = this.itemData.filter(item =>
|
||||
item.name && item.name.includes('总成本')
|
||||
);
|
||||
// 过滤出不包含“总成本”的项
|
||||
const otherItems = this.itemData.filter(item =>
|
||||
!item.name || !item.name.includes('总成本')
|
||||
);
|
||||
// 合并:总成本项在前,其他项在后
|
||||
return [...totalCostItems, ...otherItems];
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
|
||||
@@ -143,8 +143,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)',
|
||||
@@ -204,7 +204,7 @@ export default {
|
||||
color: (params) => {
|
||||
const dataIndex = params.dataIndex;
|
||||
const currentFlag = safeFlag[dataIndex] || 0; // 双重保险,防止越界
|
||||
return currentFlag === 0
|
||||
return currentFlag === 1
|
||||
? 'rgba(118, 218, 190, 1)'
|
||||
: 'rgba(249, 164, 74, 1)';
|
||||
},
|
||||
|
||||
@@ -64,12 +64,12 @@ export default {
|
||||
{ name: "燃动力", targetValue: 0, value: 0, proportion: 0, flag: 0, route:'fuelPowerCostAnalysis/fuelPowerCostAnalysis' },
|
||||
{
|
||||
name: "包装物", targetValue: 0, value: 0, proportion: 0, flag: 0,
|
||||
route:'rawMaterialCostAnalysis/rawMaterialCostAnalysis'
|
||||
route:'packagingCostAnalysis/packagingCostAnalysis'
|
||||
|
||||
},
|
||||
{
|
||||
name: "辅料", targetValue: 0, value: 0, proportion: 0, flag: 0,
|
||||
route: 'rawMaterialCostAnalysis/rawMaterialCostAnalysis'
|
||||
route: 'packagingCostAnalysis/packagingCostAnalysis'
|
||||
|
||||
},
|
||||
{
|
||||
@@ -137,13 +137,13 @@ export default {
|
||||
// 根据 flag 获取文字/进度条颜色
|
||||
getColorByFlag(flag, isProgressBar = false) {
|
||||
const colorMap = {
|
||||
0: {
|
||||
text: 'rgba(54, 181, 138, 1)',
|
||||
progress: 'rgba(103, 103, 103, 0.5)' // 进度条颜色稍浅
|
||||
},
|
||||
1: {
|
||||
text: 'rgba(98, 213, 180, 1)',
|
||||
progress: 'rgba(98, 213, 180, 1)' // 进度条颜色稍浅
|
||||
},
|
||||
0: {
|
||||
text: 'rgba(255, 132, 0, 1)',
|
||||
progress: 'rgba(255, 132, 0, 0.7)' // 进度条颜色稍浅
|
||||
progress: 'rgba(249, 164, 74, 1)' // 进度条颜色稍浅
|
||||
}
|
||||
};
|
||||
const currentFlag = flag === 1 ? 1 : 0;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div style="flex: 1">
|
||||
<Container :name="title" icon="cockpitItemIcon" size="productBasicBg" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
||||
<div class="kpi-content" style="height: 107px;padding: 14px 16px; display: flex; width: 100%;">
|
||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
||||
<div class="topItem-container" style="display: flex; flex-direction: column;gap: 16px;overflow: hidden;">
|
||||
<!-- <topItem :itemList="parentItemList" /> -->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div style="width: 100%;">
|
||||
<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="kpi-content" style=" padding: 14px 16px; display: flex;width: 100%;">
|
||||
<div class="bottom"
|
||||
style="padding: 14px 16px; height: 620px; display: flex; width: 100%;background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- <top-item /> -->
|
||||
|
||||
250
src/views/home/costComponents/profitImpactLine.vue
Normal file
250
src/views/home/costComponents/profitImpactLine.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<div class="coreBar">
|
||||
<div class="lineBottom" style="height: 100%; width: 1590px">
|
||||
<profitImpactLineChart :yName="yName" style="height: 99%; width: 1590px" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import profitImpactLineChart from './profitImpactLineChart.vue';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: { profitImpactLineChart },
|
||||
props: ['dateData','yName'],
|
||||
data() {
|
||||
return {
|
||||
isDropdownShow: false,
|
||||
selectedProfit: '原料', // 默认选中"原料"
|
||||
profitOptions: ['原料', '其他选项1', '其他选项2'], // 可根据实际需求修改选项
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
selectProfit(item) {
|
||||
this.selectedProfit = item;
|
||||
this.isDropdownShow = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 点击外部关闭下拉菜单
|
||||
const handleOutsideClick = (e) => {
|
||||
if (!this.$el.contains(e.target)) {
|
||||
this.isDropdownShow = false;
|
||||
}
|
||||
};
|
||||
document.addEventListener('click', handleOutsideClick);
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
document.removeEventListener('click', handleOutsideClick);
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.coreBar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
|
||||
.barTop {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
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;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.legend-icon {
|
||||
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;
|
||||
}
|
||||
|
||||
.achieved {
|
||||
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;
|
||||
gap: 2px;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
background: #ecf4fe;
|
||||
margin: 0;
|
||||
|
||||
.dropdown-container {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.item-button {
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
font-style: normal;
|
||||
letter-spacing: 2px;
|
||||
padding: 0 24px 0 12px;
|
||||
overflow: hidden;
|
||||
|
||||
.item-text {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.category-btn {
|
||||
width: 88px;
|
||||
border-top-left-radius: 12px;
|
||||
border-bottom-left-radius: 12px;
|
||||
background: #ffffff;
|
||||
color: #0b58ff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.profit-btn {
|
||||
width: 102px; // 保持原组件要求的宽度,其余样式与第二个组件一致
|
||||
border-top-right-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
position: relative;
|
||||
background: #ffffff;
|
||||
color: #0b58ff;
|
||||
text-align: left;
|
||||
|
||||
&.active {
|
||||
background: #3071ff;
|
||||
color: rgba(249, 252, 255, .8);
|
||||
}
|
||||
|
||||
.profit-text {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-arrow {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid currentColor;
|
||||
border-top: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&.rotate {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-options {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
margin-top: 2px;
|
||||
width: 102px; // 与按钮宽度一致
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
|
||||
.dropdown-option {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
letter-spacing: 1px;
|
||||
|
||||
&:hover {
|
||||
background: #f5f7fa;
|
||||
color: #3071ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
145
src/views/home/costComponents/profitImpactLineChart.vue
Normal file
145
src/views/home/costComponents/profitImpactLineChart.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 500px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
props: {
|
||||
yName: {
|
||||
type: String,
|
||||
default: () => '元/㎡'
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initData();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
console.error('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
const myChart = echarts.init(chartDom);
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
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: 30,
|
||||
bottom: 30, // 增大底部间距,避免柱子与X轴标签重叠
|
||||
right: 70,
|
||||
left: 40,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true, // 开启边界间隙,让柱子居中显示,不贴边
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0] // 标签向下偏移,避免与柱子底部重叠
|
||||
},
|
||||
data: ['6月', '7月', '8月', '9月', '10月', '11月']
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
// 左侧Y轴:目标/达标/未达标(数量,单位“片”)
|
||||
{
|
||||
type: 'value',
|
||||
// name: this.yName,
|
||||
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开始
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '利润',
|
||||
type: 'line',
|
||||
// yAxisIndex: 1,
|
||||
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: [200, 280, 180, 300, 220, 350],
|
||||
symbol: 'circle',
|
||||
symbolSize: 6
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
// 窗口缩放适配
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
|
||||
// 组件销毁清理
|
||||
this.$once('hook:destroyed', () => {
|
||||
window.removeEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
myChart.dispose();
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -26,7 +26,7 @@ export default {
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
dateData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
type: Object,
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
},
|
||||
yName: { // 接收父组件传递的设备数据数组
|
||||
|
||||
233
src/views/home/costComponents/profitTotalChart.vue
Normal file
233
src/views/home/costComponents/profitTotalChart.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<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>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import costBar from './profitImpactLine'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, costBar },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
trendData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
dateData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
},
|
||||
yName: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
name: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '趋势图' // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
productionOverviewVo: {
|
||||
handler(newValue, oldValue) {
|
||||
this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
}
|
||||
}
|
||||
}
|
||||
</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>
|
||||
@@ -134,12 +134,12 @@ export default {
|
||||
getColorByFlag(flag, isProgressBar = false) {
|
||||
const colorMap = {
|
||||
1: {
|
||||
text: 'rgba(54, 181, 138, 1)',
|
||||
progress: 'rgba(103, 103, 103, 0.5)' // 进度条颜色稍浅
|
||||
text: 'rgba(98, 213, 180, 1)',
|
||||
progress: 'rgba(98, 213, 180, 1)' // 进度条颜色稍浅
|
||||
},
|
||||
0: {
|
||||
text: 'rgba(255, 132, 0, 1)',
|
||||
progress: 'rgba(255, 132, 0, 0.7)' // 进度条颜色稍浅
|
||||
progress: 'rgba(249, 164, 74, 1)' // 进度条颜色稍浅
|
||||
}
|
||||
};
|
||||
const currentFlag = flag === 1 ? 1 : 0;
|
||||
|
||||
@@ -86,14 +86,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 = () => {
|
||||
@@ -104,39 +96,6 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTimeChange(data) {
|
||||
console.log(data);
|
||||
|
||||
},
|
||||
windowWidth(value) {
|
||||
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
|
||||
},
|
||||
@@ -173,37 +132,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('正在导出,请稍等!')
|
||||
// const element = document.getElementById('dayRepDom')
|
||||
// element.style.display = 'block'
|
||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
||||
// html2canvas(element, {
|
||||
// dpi: 300, // Set to 300 DPI
|
||||
// scale: 3 // Adjusts your resolution
|
||||
// }).then(function(canvas) {
|
||||
// const imgWidth = 595.28
|
||||
// const imgHeight = 841.89
|
||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
||||
// setTimeout(() => {
|
||||
// PDF.save(fileName) // 导出文件名
|
||||
// }, 1000)
|
||||
// })
|
||||
// element.style.display = 'none'
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<profitLineChart :name=" '总利润趋势图·元' " :yName="'元' " />
|
||||
<profitLineChart :name=" '总利润趋势·元' " :yName="'元' " />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,7 +54,7 @@ import { Sidebar } from "../../layout/components";
|
||||
import screenfull from "screenfull";
|
||||
import changeBase from "./costComponents/changeBase";
|
||||
import totalProfit from "./costComponents/totalProfit.vue";
|
||||
import profitLineChart from "./costComponents/profitLineChart.vue";
|
||||
import profitLineChart from "./costComponents/profitTotalChart.vue";
|
||||
import { mapState } from "vuex";
|
||||
import singleTopSelect from "./costComponents/singleTopSelect.vue";
|
||||
// import PSDO from "./components/PSDO.vue";
|
||||
|
||||
@@ -217,16 +217,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('正在导出,请稍等!')
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
gap: 12px;
|
||||
grid-template-columns: 1624px;
|
||||
">
|
||||
<changeBase />
|
||||
<changeBase @selectChange="getBase" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex; gap: 16px;margin-top: -20px;">
|
||||
@@ -25,8 +25,8 @@
|
||||
gap: 12px;
|
||||
grid-template-columns:804px 804px;
|
||||
">
|
||||
<productItemOverviewItem :parentItemList="itemLeftList" :title=" '领用汇总' " />
|
||||
<productItemOverviewItem :parentItemList="itemRightList" :title="'入账汇总'" />
|
||||
<productItemOverviewItem :parentItemList="receiveTotal" :title=" '领用汇总' " />
|
||||
<productItemOverviewItem :parentItemList="inStorageTotal" :title="'入账汇总'" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -37,8 +37,7 @@
|
||||
grid-template-columns: 804px 804px;
|
||||
">
|
||||
<productLeftTable :tableData="receiveDetailList" />
|
||||
<productRightTable :tableData="accountSumaryList"/>
|
||||
|
||||
<productRightTable :tableData="accountSumaryList" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -82,8 +81,11 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
value: 100,
|
||||
levelId:1,
|
||||
accountSumaryList: [],
|
||||
receiveDetailList:[],
|
||||
receiveDetailList: [],
|
||||
receiveTotal: [],
|
||||
inStorageTotal: [],
|
||||
itemLeftList: [
|
||||
{ name: "备品备件", value: 137,},
|
||||
{
|
||||
@@ -168,23 +170,38 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getBase(data) {
|
||||
console.log(data);
|
||||
this.levelId = data
|
||||
this.getData()
|
||||
},
|
||||
getData() {
|
||||
getReceiveDetailPage({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
levelId: 1
|
||||
levelId: this.levelId
|
||||
}).then((res) => {
|
||||
console.log(res);
|
||||
this.receiveDetailList = res.data.领用明细
|
||||
this.receiveDetailList = res.data.receiveDetail
|
||||
this.receiveTotal = res.data.receiveTotal.map((item) => {
|
||||
const name = Object.keys(item)[0];
|
||||
const value = item[name];
|
||||
console.log(name);
|
||||
return { name, value };
|
||||
});
|
||||
})
|
||||
getAccountSumaryPage({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
levelId: 1
|
||||
levelId: this.levelId
|
||||
}).then((res) => {
|
||||
console.log(res);
|
||||
this.accountSumaryList = res.data.入账明细
|
||||
|
||||
this.accountSumaryList = res.data.inStorageDetail
|
||||
this.inStorageTotal = res.data.inStorageTotal.map((item) => {
|
||||
const name = Object.keys(item)[0];
|
||||
const value = item[name];
|
||||
return { name, value };
|
||||
});
|
||||
})
|
||||
},
|
||||
handleClickOutside() {
|
||||
|
||||
Reference in New Issue
Block a user