修改集团及基地图表样式

This commit is contained in:
2026-03-20 16:13:53 +08:00
parent 2465f89d26
commit bb66f97b95
257 changed files with 25365 additions and 6814 deletions

View File

@@ -19,9 +19,9 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div>
</div>
<div class="electricityGauge">
<!-- <div class="electricityGauge">
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
</div>
</div> -->
</div>
<div class="line" style="padding: 0px;">
<verticalBarChart :detailData="monthData">
@@ -142,33 +142,27 @@ export default {
}
.number {
display: flex;
align-items: center;
gap: 6px;
// width: 190px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
font-size: 46px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
text-align: center;
font-style: normal;
white-space: nowrap;
margin-top: 20px;
}
.mom {
// width: 97px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
font-size: 20px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
text-align: center;
font-style: normal;
z-index: 1000;
margin-top: 20px;
}
}

View File

@@ -2,33 +2,21 @@
<div style="flex: 1">
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<div class="topItem-container" style="display: flex; gap: 8px; width: 100%;">
<!-- 管理费用模块 -->
<div class="dashboard">
<div class="topItem-container" style="display: flex; gap: 8px">
<div
v-for="item in sortedIndicators"
:key="item.key"
class="dashboard"
@click="item.route && handleDashboardClick(item.route)"
>
<div class="title">
管理费用·万元
{{ item.name }}·{{ item.unit }}
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="manageCostData"></operatingSingleBar>
</div>
</div>
<!-- 销售费用模块 -->
<div class="dashboard">
<div class="title">
销售费用·万元
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="saleCostData"></operatingSingleBar>
</div>
</div>
<!-- 财务费用模块 -->
<div class="dashboard">
<div class="title">
财务费用·万元
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="financeCostData"></operatingSingleBar>
<div style='font-size: 16px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{item.detailData.rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:item.detailData.flag>0?'#30B590':'#FF9423'}" >{{item.detailData.diff}}</span></span>
</div>
<operatingSingleBar :detailData="item.detailData"></operatingSingleBar>
</div>
</div>
</div>
@@ -63,69 +51,66 @@ export default {
},
},
data() {
return {
// 初始化费用数据包含flag字段
manageCostData: { title: "管理费用", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
saleCostData: { title: "销售费用", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
financeCostData: { title: "财务费用", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
}
return {}
},
computed: {
indicatorDefs() {
return [
{ key: 'mgmtFee', name: '管理费用', unit: '万元'},
{ key: 'salesFee', name: '销售费用', unit: '万元'},
{ key: 'finFee', name: '财务费用', unit: '万元'}
]
},
indicators() {
let _this = this
const fallback = { budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
const list = (Array.isArray(_this.monthAnalysis) ? _this.monthAnalysis : [])
return _this.indicatorDefs.map(def => {
const data = list.find(item => item && item.title === def.name) || fallback
const detailData = {
...data,
flag: _this.getRateFlag(data.rate, data.real, data.budget),
}
return {
...def,
detailData,
sortValue: Number((data && data.real) ?? 0)
}
})
},
sortedIndicators() {
const unitOrder = ['万元']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx
}
return this.indicators.slice().sort((a, b) => {
const ur = unitRank(a.unit) - unitRank(b.unit)
if (ur !== 0) return ur
const vr = (b.sortValue ?? -Infinity) - (a.sortValue ?? -Infinity)
if (vr !== 0) return vr
return String(a.key).localeCompare(String(b.key))
})
},
},
watch: {
monthAnalysis: {
handler(newVal) {
this.updateCostData(newVal)
},
handler(newVal) {},
deep: true,
immediate: true // 初始化立即执行
}
},
mounted() {
this.updateCostData(this.monthAnalysis)
},
mounted() {},
methods: {
// 达标标识判断≥100返回1<100返回0
getRateFlag(rate, real, target) {
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 1. 完成率 >= 100 => 达标
if (rate >= 100) return 1;
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
if (rate === 0 && target === 0) return 1;
// 其他情况 => 未达标
return 0;
},
// 处理费用数据
updateCostData(data) {
// 数据兜底确保是数组且长度≥3
const validData = Array.isArray(data) && data.length >= 3
? data
: this.$props.monthAnalysis;
// 提取三个费用项数据(兜底处理)
const manageItem = validData[0] || { title: "管理费用", budget: 0, real: 0, rate: 0, diff: 0 };
const saleItem = validData[1] || { title: "销售费用", budget: 0, real: 0, rate: 0, diff: 0 };
const financeItem = validData[2] || { title: "财务费用", budget: 0, real: 0, rate: 0, diff: 0 };
// 整合flag字段
this.manageCostData = {
...manageItem,
flag: this.getRateFlag(manageItem.rate, manageItem.real, manageItem.budget)
};
this.saleCostData = {
...saleItem,
flag: this.getRateFlag(saleItem.rate, saleItem.real, saleItem.budget)
};
this.financeCostData = {
...financeItem,
flag: this.getRateFlag(financeItem.rate, financeItem.real, financeItem.budget)
};
// 调试日志
console.log('管理费用数据:', this.manageCostData);
console.log('销售费用数据:', this.saleCostData);
console.log('财务费用数据:', this.financeCostData);
getRateFlag(rate, real, target) {
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 1. 完成率 >= 100 => 达标
if (rate >= 100) return 1;
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
if (rate === 0 && target === 0) return 1;
// 其他情况 => 未达标
return 0;
}
}
}
@@ -169,7 +154,6 @@ getRateFlag(rate, real, target) {
line-height: 18px;
letter-spacing: 2px;
text-align: left;
margin-bottom: 12px;
}
// 图表容器:适配高度

View File

@@ -39,7 +39,7 @@
</div>
</div>
<div class="lineBottom" style="height: 100%; width: 100%">
<operatingLineBar :dateData="dateData" :chartData="chartD" style="height: 99%; width: 100%" />
<operatingLineBar :dateData="dateData" :chartData="chartD" style="height: 100%; width: 100%" />
</div>
</div>
</template>
@@ -152,7 +152,7 @@ export default {
name: '预算',
type: 'bar',
yAxisIndex: 0,
barWidth: 14,
barWidth: 40,
itemStyle: {
color: {
type: 'linear',
@@ -172,13 +172,13 @@ export default {
name: '实际',
type: 'bar',
yAxisIndex: 0,
barWidth: 14,
barWidth: 40,
label: {
show: true,
position: 'top',
offset: [30, 0],
width: 68,
height: 20,
offset: [32, 0],
width: 100,
height: 22,
formatter: (params) => {
const diff = data.diff || [];
const flags = data.flags || [];
@@ -212,23 +212,21 @@ export default {
borderRadius: 4,
borderColor: '#BFCBD577',
borderWidth: 0,
lineHeight: 20,
lineHeight: 26,
rich: {
text: {
width: 'auto',
padding: [5, 10, 5, 0],
align: 'center',
color: '#464646',
fontSize: 11,
lineHeight: 20
fontSize: 14
},
achieved: {
width: 'auto',
padding: [5, 0, 5, 10],
align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致
fontSize: 11,
lineHeight: 20
fontSize: 14
},
// 未达标样式
unachieved: {
@@ -236,8 +234,7 @@ export default {
padding: [5, 0, 5, 10],
align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
fontSize: 11,
lineHeight: 20
fontSize: 14
}
}
},

View File

@@ -133,11 +133,12 @@ export default {
}
}
},
grid: {
grid: {
top: 30,
bottom: 30,
right: 70,
left: 60
bottom: 5,
right: 20,
left: 25,
containLabel: true
},
xAxis: [
{

View File

@@ -1,5 +1,5 @@
<template>
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 380px;"></div>
</template>
<script>
import * as echarts from 'echarts';
@@ -85,7 +85,7 @@ export default {
},
grid: {
top: 30,
bottom: 5,
bottom: 20,
right: 10,
left: 25,
containLabel: true

View File

@@ -85,8 +85,8 @@ export default {
},
grid: {
top: 25,
bottom: 30,
right: 0,
bottom: 25,
right: 10,
left: 2,
containLabel: true
},

View File

@@ -10,17 +10,20 @@
background-color: rgba(249, 252, 255, 1);
flex-direction: column;
">
<div style="
font-weight: 400;
<div style="
padding: 16px 16px 5px 16px;
line-height: 18px;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
font-style: normal;
padding: 16px 0 0 16px;
">
集团情况
</div>
集团情况
</div>
<div style='font-size: 16px;line-height: 16px;text-align: right;padding-right: 16px;'>
<span>完成率:<span style='color: #0B58FF;'>{{chartData.group.rate[0]}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:chartData.group.flags>0?'#30B590':'#FF9423'}" >{{chartData.group.diff[0]}}</span></span>
</div>
<operatingTopBar :chartData="chartData" />
</div>
<div class="right" style="

View File

@@ -10,17 +10,20 @@
background-color: rgba(249, 252, 255, 1);
flex-direction: column;
">
<div style="
font-weight: 400;
<div style="
padding: 16px 16px 5px 16px;
line-height: 18px;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
font-style: normal;
padding: 16px 0 0 16px;
">
集团情况
</div>
集团情况
</div>
<div style='font-size: 16px;line-height: 16px;text-align: right;padding-right: 16px;'>
<span>完成率:<span style='color: #0B58FF;'>{{chartData.group.rate[0]}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:chartData.group.flags>0?'#30B590':'#FF9423'}" >{{chartData.group.diff[0]}}</span></span>
</div>
<operatingTopBar :chartData="chartData" />
</div>
<div class="right" style="

View File

@@ -1,6 +1,6 @@
<template>
<div class="lineBottom" style="height: 180px; width: 100%">
<operatingLineBarSaleSingle :refName="'totalOperating'" :chartData="chartD" style="height: 99%; width: 100%" />
<div class="lineBottom" style="height: 160px; width: 100%">
<operatingLineBarSaleSingle :refName="'totalOperating'" :chartData="chartD" style="height: 100%; width: 100%" />
</div>
</template>
@@ -25,7 +25,7 @@ export default {
// const bgImageUrl = require('@/assets/img/labelBg.png');
const rate = this.detailData?.rate || 0
const diff = this.detailData?.diff || 0
console.log('diff', diff);
console.log('detailData========================', this.detailData);
const seriesData = [
{
@@ -35,59 +35,6 @@ export default {
show: true,
position: 'top',
offset: [0, 0],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: function (params) {
return `{value|完成率}{rate|${rate}%}`;
},
// 核心样式匹配CSS需求
backgroundColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' }, // 顶部0px位置阴影最强
// { offset: 0.1, color: 'rgba(205, 215, 224, 0.4)' }, // 1px位置阴影减弱对应1px
// { offset: 0.15, color: 'rgba(205, 215, 224, 0.6)' }, // 3px位置阴影几乎消失对应3px扩散
{ offset: 0.2, color: '#ffffff' }, // 主体白色
{ offset: 1, color: '#ffffff' }
]
},
// 外阴影0px 2px 2px 0px rgba(191,203,215,0.5)
shadowColor: 'rgba(191,203,215,0.5)',
shadowBlur: 2,
shadowOffsetX: 0,
shadowOffsetY: 2,
// 圆角4px
borderRadius: 4,
// 移除边框
borderColor: '#BFCBD577',
borderWidth: 0,
// 文字垂直居中(针对富文本)
lineHeight: 20,
rich: {
value: {
// 缩小宽度和内边距适配68px容器
width: 'auto', // 自动宽度替代固定40px
padding: [5, 0, 5, 10], // 缩小内边距
align: 'center',
color: '#464646', // 文字灰色
fontSize: 11, // 缩小字体,适配小尺寸
lineHeight: 20 // 垂直居中
},
rate: {
width: 'auto',
padding: [5, 10, 5, 0],
align: 'center',
color: '#0B58FF', // 数字蓝色
fontSize: 11,
lineHeight: 20
}
}
},
itemStyle: {
color: {
@@ -109,69 +56,6 @@ export default {
show: true,
position: 'top',
offset: [0, 0],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: (params) => {
// const flags = flags || [];
const currentDiff = diff || 0;
const currentFlag = this.detailData?.flag || 0;
// const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`;
} else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`;
}
},
backgroundColor: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
{ offset: 0.2, color: '#ffffff' },
{ offset: 1, color: '#ffffff' }
]
},
shadowColor: 'rgba(191,203,215,0.5)',
shadowBlur: 2,
shadowOffsetX: 0,
shadowOffsetY: 2,
borderRadius: 4,
borderColor: '#BFCBD577',
borderWidth: 0,
lineHeight: 20,
rich: {
text: {
width: 'auto',
padding: [5, 10, 5, 0],
align: 'center',
color: '#464646',
fontSize: 11,
lineHeight: 20
},
achieved: {
width: 'auto',
padding: [5, 0, 5, 10],
align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致
fontSize: 11,
lineHeight: 20
},
// 未达标样式
unachieved: {
width: 'auto',
padding: [5, 0, 5, 10],
align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
fontSize: 11,
lineHeight: 20
}
}
},
itemStyle: {
borderRadius: [4, 4, 0, 0],
@@ -185,11 +69,12 @@ export default {
series: [
{
type: 'bar',
barWidth: 24,
barWidth: 60,
barCategoryGap: '50%',
data: seriesData,
itemStyle: {
color: (params) => {
console.log('params=================', params);
const currentFlag = params.data.flag || 0;
return currentFlag === 1
? {

View File

@@ -73,65 +73,9 @@ export default {
yAxisIndex: 0, // 左侧Y轴万元
label: {
show: true,
position: 'top',
offset: [-30, 0],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: (params) => {
// const { rate = 0, diff = 0 } = params.data || {};
return `{value|完成率}{rate|${rate}%}`;
},
// formatter: `{value|完成率}{rate|${rate}%}`,
// 核心样式匹配CSS需求
backgroundColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' }, // 顶部0px位置阴影最强
// { offset: 0.1, color: 'rgba(205, 215, 224, 0.4)' }, // 1px位置阴影减弱对应1px
// { offset: 0.15, color: 'rgba(205, 215, 224, 0.6)' }, // 3px位置阴影几乎消失对应3px扩散
{ offset: 0.2, color: '#ffffff' }, // 主体白色
{ offset: 1, color: '#ffffff' }
]
},
// 外阴影0px 2px 2px 0px rgba(191,203,215,0.5)
shadowColor: 'rgba(191,203,215,0.5)',
shadowBlur: 2,
shadowOffsetX: 0,
shadowOffsetY: 2,
// 圆角4px
borderRadius: 4,
// 移除边框
borderColor: '#BFCBD577',
borderWidth: 0,
// 文字垂直居中(针对富文本)
lineHeight: 20,
rich: {
value: {
// 缩小宽度和内边距适配68px容器
width: 'auto', // 自动宽度替代固定40px
padding: [5, 0, 5, 10], // 缩小内边距
align: 'center',
color: '#464646', // 文字灰色
fontSize: 11, // 缩小字体,适配小尺寸
lineHeight: 20 // 垂直居中
},
rate: {
width: 'auto',
padding: [5, 10, 5, 0],
align: 'center',
color: '#0B58FF', // 数字蓝色
fontSize: 11,
lineHeight: 20
}
}
position: 'top'
},
barWidth: 14,
barWidth: 65,
itemStyle: {
color: {
type: 'linear',
@@ -153,73 +97,9 @@ export default {
yAxisIndex: 0,
label: {
show: true,
position: 'top',
offset: [30, 0],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: (params) => {
// const flags = flags || [];
const currentDiff = diff || 0;
const currentFlag = data.flags[0] || 0;
// const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`;
} else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`;
}
},
backgroundColor: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
{ offset: 0.2, color: '#ffffff' },
{ offset: 1, color: '#ffffff' }
]
},
shadowColor: 'rgba(191,203,215,0.5)',
shadowBlur: 2,
shadowOffsetX: 0,
shadowOffsetY: 2,
borderRadius: 4,
borderColor: '#BFCBD577',
borderWidth: 0,
lineHeight: 20,
rich: {
text: {
width: 'auto',
padding: [5, 10, 5, 0],
align: 'center',
color: '#464646',
fontSize: 11,
lineHeight: 20
},
achieved: {
width: 'auto',
padding: [5, 0, 5, 10],
align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致
fontSize: 11,
lineHeight: 20
},
// 未达标样式
unachieved: {
width: 'auto',
padding: [5, 0, 5, 10],
align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
fontSize: 11,
lineHeight: 20
}
}
position: 'top'
},
barWidth: 14,
barWidth: 65,
itemStyle: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)

View File

@@ -19,9 +19,9 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div>
</div>
<div class="electricityGauge">
<!-- <div class="electricityGauge">
<electricityGauge :id="'totalG'" :detailData="ytdData" id="totalGauge"></electricityGauge>
</div>
</div> -->
</div>
<div class="line" style="padding: 0px;">
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
@@ -142,32 +142,26 @@ export default {
}
.number {
display: flex;
align-items: center;
gap: 8px;
// width: 190px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
font-size: 46px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
text-align: center;
font-style: normal;
margin-top: 20px;
}
.mom {
// width: 97px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
font-size: 20px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
text-align: center;
font-style: normal;
z-index: 1000;
margin-top: 20px;
}
}

View File

@@ -1,5 +1,11 @@
<template>
<div :ref="refName" id="coreLineChart" style="width: 100%; height: 210px;"></div>
<div style="width: 100%; height: 210px;position: relative;">
<div style='font-size: 16px;position: absolute;right: 20px;top:10px'>
<span>完成率:<span style='color: #0B58FF;'>{{detailData.rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:flag>0?'#30B590':'#FF9423'}" >{{detailData.diff}}</span></span>
</div>
<div :ref="refName" id="coreLineChart" style="width: 100%; height: 210px;"></div>
</div>
</template>
<script>
import * as echarts from 'echarts';
@@ -8,7 +14,8 @@ export default {
components: {},
data() {
return {
myChart: null // 存储图表实例,避免重复创建
myChart: null, // 存储图表实例,避免重复创建
flag:0
};
},
props: {
@@ -43,18 +50,18 @@ export default {
}
},
methods: {
getRateFlag(rate, real, target) {
if (isNaN(rate) || rate === null || rate === undefined) return 0;
getRateFlag(rate, real, target) {
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 1. 完成率 >= 100 => 达标
if (rate >= 100) return 1;
// 1. 完成率 >= 100 => 达标
if (rate >= 100) return 1;
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
if (rate === 0 && target === 0) return 1;
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
if (rate === 0 && target === 0) return 1;
// 其他情况 => 未达标
return 0;
},
// 其他情况 => 未达标
return 0;
},
updateChart() {
const chartDom = this.$refs[this.refName];
if (!chartDom) {
@@ -70,6 +77,7 @@ getRateFlag(rate, real, target) {
const diff = this.detailData.diff || 0
const rate = this.detailData.rate || 0
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0
this.flag = flagValue
const option = {
tooltip: {
@@ -92,10 +100,10 @@ getRateFlag(rate, real, target) {
// }
},
grid: {
top: 10,
bottom: 30,
top: 40,
bottom: 15,
right: 80,
left: 30,
left: 10,
containLabel: true,
show: false // 隐藏grid背景避免干扰
},
@@ -153,70 +161,7 @@ getRateFlag(rate, real, target) {
label: {
show: true,
position: 'right',
offset: [0, 25],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: (params) => {
// const flags = flags || [];
const currentDiff = diff || 0;
const currentFlag = flagValue || 0;
// const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`;
} else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`;
}
},
backgroundColor: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
{ offset: 0.2, color: '#ffffff' },
{ offset: 1, color: '#ffffff' }
]
},
shadowColor: 'rgba(191,203,215,0.5)',
shadowBlur: 2,
shadowOffsetX: 0,
shadowOffsetY: 2,
borderRadius: 4,
borderColor: '#BFCBD577',
borderWidth: 0,
lineHeight: 20,
rich: {
text: {
width: 'auto',
padding: [5, 10, 5, 0],
align: 'center',
color: '#464646',
fontSize: 11,
lineHeight: 20
},
achieved: {
width: 'auto',
padding: [5, 0, 5, 10],
align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致
fontSize: 11,
lineHeight: 20
},
// 未达标样式
unachieved: {
width: 'auto',
padding: [5, 0, 5, 10],
align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
fontSize: 11,
lineHeight: 20
}
}
fontSize: 14
},
itemStyle: {
color: flagValue === 1
@@ -243,60 +188,7 @@ getRateFlag(rate, real, target) {
label: {
show: true,
position: 'right',
offset: [0, 25],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: function (params) {
return `{value|完成率}{rate|${rate}%}`;
},
// 核心样式匹配CSS需求
backgroundColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' }, // 顶部0px位置阴影最强
// { offset: 0.1, color: 'rgba(205, 215, 224, 0.4)' }, // 1px位置阴影减弱对应1px
// { offset: 0.15, color: 'rgba(205, 215, 224, 0.6)' }, // 3px位置阴影几乎消失对应3px扩散
{ offset: 0.2, color: '#ffffff' }, // 主体白色
{ offset: 1, color: '#ffffff' }
]
},
// 外阴影0px 2px 2px 0px rgba(191,203,215,0.5)
shadowColor: 'rgba(191,203,215,0.5)',
shadowBlur: 2,
shadowOffsetX: 0,
shadowOffsetY: 2,
// 圆角4px
borderRadius: 4,
// 移除边框
borderColor: '#BFCBD577',
borderWidth: 0,
// 文字垂直居中(针对富文本)
lineHeight: 20,
rich: {
value: {
// 缩小宽度和内边距适配68px容器
width: 'auto', // 自动宽度替代固定40px
padding: [5, 0, 5, 10], // 缩小内边距
align: 'center',
color: '#464646', // 文字灰色
fontSize: 11, // 缩小字体,适配小尺寸
lineHeight: 20 // 垂直居中
},
rate: {
width: 'auto',
padding: [5, 10, 5, 0],
align: 'center',
color: '#0B58FF', // 数字蓝色
fontSize: 11,
lineHeight: 20
}
}
fontSize: 14
},
itemStyle: {
// 预算的渐变颜色(蓝系渐变)

View File

@@ -2,33 +2,21 @@
<div style="flex: 1">
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<div class="topItem-container" style="display: flex; gap: 8px; width: 100%;">
<!-- 管理费用模块 -->
<div class="dashboard">
<div class="topItem-container" style="display: flex; gap: 8px">
<div
v-for="item in sortedIndicators"
:key="item.key"
class="dashboard"
@click="item.route && handleDashboardClick(item.route)"
>
<div class="title">
管理费用·万元
{{ item.name }}·{{ item.unit }}
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="manageCostData"></operatingSingleBar>
</div>
</div>
<!-- 销售费用模块 -->
<div class="dashboard">
<div class="title">
销售费用·万元
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="saleCostData"></operatingSingleBar>
</div>
</div>
<!-- 财务费用模块 -->
<div class="dashboard">
<div class="title">
财务费用·万元
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="financeCostData"></operatingSingleBar>
<div style='font-size: 16px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{item.detailData.rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:item.detailData.flag>0?'#30B590':'#FF9423'}" >{{item.detailData.diff}}</span></span>
</div>
<operatingSingleBar :detailData="item.detailData"></operatingSingleBar>
</div>
</div>
</div>
@@ -70,63 +58,64 @@ export default {
financeCostData: { title: "财务费用", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
}
},
computed: {
indicatorDefs() {
return [
{ key: 'mgmtFee', name: '管理费用', unit: '万元'},
{ key: 'salesFee', name: '销售费用', unit: '万元'},
{ key: 'finFee', name: '财务费用', unit: '万元'}
]
},
indicators() {
let _this = this
const fallback = { budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
const list = (Array.isArray(_this.ytdAnalysis) ? _this.ytdAnalysis : [])
return _this.indicatorDefs.map(def => {
const data = list.find(item => item && item.title === def.name) || fallback
const detailData = {
...data,
flag: _this.getRateFlag(data.rate, data.real, data.budget),
}
return {
...def,
detailData,
sortValue: Number((data && data.real) ?? 0)
}
})
},
sortedIndicators() {
const unitOrder = ['万元']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx
}
return this.indicators.slice().sort((a, b) => {
const ur = unitRank(a.unit) - unitRank(b.unit)
if (ur !== 0) return ur
const vr = (b.sortValue ?? -Infinity) - (a.sortValue ?? -Infinity)
if (vr !== 0) return vr
return String(a.key).localeCompare(String(b.key))
})
},
},
watch: {
ytdAnalysis: {
handler(newVal) {
this.updateCostData(newVal)
},
handler(newVal) {},
deep: true,
immediate: true // 初始化立即执行
}
},
mounted() {
this.updateCostData(this.ytdAnalysis)
},
mounted() {},
methods: {
// 达标标识判断≥100返回1<100返回0
getRateFlag(rate, real, target) {
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 1. 完成率 >= 100 => 达标
if (rate >= 100) return 1;
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
if (rate === 0 && target === 0) return 1;
// 其他情况 => 未达标
return 0;
},
// 处理费用数据
updateCostData(data) {
// 数据兜底确保是数组且长度≥3
const validData = Array.isArray(data) && data.length >= 3
? data
: this.$props.ytdAnalysis;
// 提取三个费用项数据(兜底处理)
const manageItem = validData[0] || { title: "管理费用", budget: 0, real: 0, rate: 0, diff: 0 };
const saleItem = validData[1] || { title: "销售费用", budget: 0, real: 0, rate: 0, diff: 0 };
const financeItem = validData[2] || { title: "财务费用", budget: 0, real: 0, rate: 0, diff: 0 };
// 整合flag字段
this.manageCostData = {
...manageItem,
flag: this.getRateFlag(manageItem.rate, manageItem.real, manageItem.budget)
};
this.saleCostData = {
...saleItem,
flag: this.getRateFlag(saleItem.rate, saleItem.real, saleItem.budget)
};
this.financeCostData = {
...financeItem,
flag: this.getRateFlag(financeItem.rate, financeItem.real, financeItem.budget)
};
// 调试日志
console.log('管理费用数据:', this.manageCostData);
console.log('销售费用数据:', this.saleCostData);
console.log('财务费用数据:', this.financeCostData);
getRateFlag(rate, real, target) {
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 1. 完成率 >= 100 => 达标
if (rate >= 100) return 1;
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
if (rate === 0 && target === 0) return 1;
// 其他情况 => 未达标
return 0;
}
}
}
@@ -170,7 +159,6 @@ getRateFlag(rate, real, target) {
line-height: 18px;
letter-spacing: 2px;
text-align: left;
margin-bottom: 12px;
}
// 图表容器:适配高度