达标函数删除,图表label置于最顶层

This commit is contained in:
2026-04-14 10:17:38 +08:00
parent 0d74e762ce
commit 7135ab0e4b
208 changed files with 3567 additions and 12787 deletions

View File

@@ -128,10 +128,6 @@ export default {
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
}, },
methods: { methods: {
// sortChange(value) {
// this.sort = value
// this.getData()
// },
getData() { getData() {
getAccountsReceivableData({ getAccountsReceivableData({
startTime: this.dateData.startTime, startTime: this.dateData.startTime,
@@ -140,15 +136,8 @@ export default {
index: undefined, index: undefined,
factory: undefined factory: undefined
}).then((res) => { }).then((res) => {
console.log('res==============================',res);
this.monthData= res.data.month this.monthData= res.data.month
this.ytdData = res.data.ytd this.ytdData = res.data.ytd
// this.saleData = res.data.SaleData
// this.premiumProduct = res.data.premiumProduct
// this.salesTrendMap = res.data.salesTrendMap
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
}) })
}, },
handleTimeChange(obj) { handleTimeChange(obj) {
@@ -198,28 +187,7 @@ export default {
return false; return false;
} }
screenfull.toggle(this.$refs.dayReportB); screenfull.toggle(this.$refs.dayReportB);
}, }
// 导出
// 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> </script>

View File

@@ -1,298 +0,0 @@
<template>
<div style="flex: 1">
<Container name="数据趋势" icon="cockpitItemIcon" size="opLargeBg" topSize="large">
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%; gap: 16px">
<div class="right" style="
height: 191px;
display: flex;
width: 1595px;
background-color: rgba(249, 252, 255, 1);
">
<dataTrendBar @changeItem="handleChange" :chartData="chartData" />
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from "../components/container.vue";
import dataTrendBar from "./dataTrendBar.vue";
export default {
name: "ProductionStatus",
components: { Container, dataTrendBar },
props: {
trend: {
type: Array,
// 默认值与实际数据结构一致12个月
default: () => [
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
]
},
},
data() {
return {
chartData: {
months: [], // 月份数组2025年01月 - 2025年12月
rates: [], // 每月完成率(百分比)
reals: [], // 每月实际值
budgets: [],// 每月预算值
diffs: [], // 每月差值
flags: [] // 每月达标标识≥100 → 1<100 → 0
}
};
},
watch: {
trend: {
handler(newVal) {
this.processTrendData(newVal);
},
immediate: true,
deep: true,
},
},
mounted() {
this.processTrendData(this.trend);
},
methods: {
handleChange(value) {
this.$emit("handleChange", value);
},
/**
* 处理趋势数据适配12个月的数组结构
* @param {Array} trendData - 原始趋势数组12个月
*/
processTrendData(trendData) {
// 数据兜底确保是数组且长度为12
const validTrend = Array.isArray(trendData)
? trendData
: []
// 初始化空数组
const months = [];
const rates = [];
const reals = [];
const budgets = [];
const diffs = [];
const flags = [];
// 遍历12个月数据
validTrend.forEach(item => {
// 基础数据提取(兜底处理)
const month = item.title ?? '';
const budget = Number(item.budget) || 0;
const real = Number(item.real) || 0;
const rate = Number(item.rate) || 0;
const diff = Number(item.diff) || 0;
// 计算达标标识≥100 → 1<100 → 0
const flag = this.getRateFlag(rate, real, budget);
// 填充数组
months.push(month);
rates.push(rate); // 转为百分比并取整
reals.push(real);
budgets.push(budget);
diffs.push(diff);
flags.push(flag);
});
// 更新chartData响应式
this.chartData = {
months,
rates,
reals,
budgets,
diffs,
flags
};
console.log('处理后的趋势数据:', this.chartData);
},
/**
* 计算达标标识
* @param {Number} rate - 完成率原始值如1.2 → 120%
* @returns {Number} 1: 达标≥100%0: 未达标(<100%
*/
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;
},
},
};
</script>
<style lang="scss" scoped>
/* 滚动容器样式 */
.scroll-container {
max-height: 210px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-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 样式 */
.production-status-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.production-status-chart-tooltip * {
color: #fff !important;
}
</style>

View File

@@ -1,477 +0,0 @@
<template>
<div class="coreBar">
<!-- 新增行容器包裹各基地情况和barTop -->
<div class="header-row">
<div class="barTop">
<!-- 关键新增右侧容器包裹图例和按钮组实现整体靠右 -->
<div class="right-container">
<div class="legend">
<span class="legend-item">
<span class="legend-icon line yield"></span>
完成率
</span>
<span class="legend-item">
<span class="legend-icon square target"></span>
预算
</span>
<span class="legend-item">
<span class="legend-icon square achieved"></span>
实际·达标
</span>
<span class="legend-item">
<span class="legend-icon square unachieved"></span>
实际·未达标
</span>
</div>
<div class="button-group">
<div class="item-button category-btn">
<span class="item-text">类目选择</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="dropdown-arrow" :class="{ 'rotate': isDropdownShow }"></span>
</div>
<div class="dropdown-options" v-if="isDropdownShow">
<div class="dropdown-option" v-for="(item, index) in profitOptions" :key="index"
@click.stop="selectProfit(item)">
{{ item }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="lineBottom" style="height: 100%; width: 100%">
<operatingLineBar :chartData="chartD" style="height: 99%; width: 100%" />
</div>
</div>
</template>
<script>
import operatingLineBar from './operatingLineBarSale.vue';
import * as echarts from 'echarts';
export default {
name: "Container",
components: { operatingLineBar },
props: ["chartData"],
data() {
return {
activeButton: 0,
isDropdownShow: false,
selectedProfit: '营业收入', // 选中的名称初始为null
profitOptions: [
'营业收入',
'单价',
'销量',
]
};
},
computed: {
// profitOptions() {
// return this.categoryData.map(item => item.name) || [];
// },
currentDataSource() {
console.log('yyyy', this.chartData);
return this.chartData
},
locations() {
console.log('this.chartData', this.chartData);
return this.chartData.months
},
// 根据按钮切换生成对应的 chartData
chartD() {
const data = this.currentDataSource;
console.log(this.currentDataSource, 'currentDataSource');
const salesData = {
allPlaceNames: this.locations,
series: [
// 1. 完成率(折线图)
{
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(40, 138, 255, 0)' }
])
},
data: data.rates, // 完成率(%
symbol: 'circle',
symbolSize: 6
},
// 2. 目标(柱状图)
{
name: '预算',
type: 'bar',
yAxisIndex: 0, // 左侧Y轴万元
barWidth: 14,
itemStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
]
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.budgets // 目标销量(万元)
},
// 3. 实际(柱状图,含达标状态)
{
name: '实际',
type: 'bar',
yAxisIndex: 0,
barWidth: 14,
label: {
show: true,
position: 'top',
offset: [0, 0],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: (params) => {
const diff = data.diffs || [];
const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0;
const currentFlag = flags[params.dataIndex] || 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: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)
const safeFlag = data.flags;
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals // 实际销量(万元)
}
]
};
// 根据按钮状态返回对应数据
return salesData;
}
},
methods: {
selectProfit(item) {
this.selectedProfit = item;
this.isDropdownShow = false;
this.$emit("changeItem", item);
}
},
};
</script>
<style scoped lang="scss">
.coreBar {
display: flex;
flex-direction: column;
width: 100%;
padding: 12px;
// 新增:头部行容器,实现一行排列
.header-row {
display: flex;
justify-content: flex-end; // 左右两端对齐
align-items: center; // 垂直居中
// width: 100%;
margin-bottom: 8px; // 与下方图表区保留间距(可根据需求调整)
}
// 各基地情况标题样式
.base-title {
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
font-style: normal;
padding: 0 0 0 16px; // 保留原有内边距
white-space: nowrap; // 防止文字换行
}
.barTop {
// 移除原有flex和justify-content由header-row控制
width: auto; // 自适应宽度
// 保留原有align-items确保内部元素垂直居中
align-items: center;
gap: 16px;
// 1. 右侧容器:包裹图例和按钮组,整体靠右
.right-container {
display: flex;
align-items: center; // 图例和按钮组垂直居中
gap: 24px; // 图例与按钮组的间距,避免贴紧
margin-right: 46px; // 右侧整体留边,与原按钮组边距一致
}
// 2. 图例:在右侧容器内横向排列
.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);
}
// 3. 按钮组:在右侧容器内,保留原有样式
.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;
overflow: hidden;
.item-text {
display: inline-block;
}
}
.category-btn {
width: 75px;
border-top-left-radius: 12px;
border-bottom-left-radius: 12px;
background: #ffffff;
color: #0b58ff;
text-align: center;
}
.profit-btn {
width: 123px;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
position: relative;
padding: 0 18px 0 8px;
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: 123px;
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>

View File

@@ -1,204 +0,0 @@
<template>
<div style="flex: 1">
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
<!-- 1. 移除 .kpi-content 的固定高度改为自适应 -->
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<!-- 新增topItem 专属包裹容器统一控制样式和布局 -->
<div class="topItem-container" style="display: flex; gap: 8px;">
<div class="dashboard">
<div class="title">
{{ month }}月完成率
</div>
<div class="number">
<div class="yield">
{{ monthData?.rate || 0 }}%
</div>
<div class="mom">
环比{{ monthData?.momRate }}%
<img v-if="monthData?.momRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div>
</div>
<!-- <div class="electricityGauge">
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
</div> -->
</div>
<div class="line" style="padding: 0px;">
<verticalBarChart :detailData="monthData">
</verticalBarChart>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default {
name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart },
// mixins: [resize],
props: {
monthData: { // 接收父组件传递的设备数据数组
type: Object,
default: () => {} // 默认空数组,避免报错
},
title: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
month: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
},
data() {
return {
chart: null,
}
},
watch: {
// itemData: {
// handler(newValue, oldValue) {
// // this.updateChart()
// },
// 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())
},
methods: {
}
}
</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;
}
.dashboard {
width: 264px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
.title {
// width: 190px;
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;
letter-spacing: 2px;
}
.number {
font-family: YouSheBiaoTiHei;
font-size: 46px;
color: #0B58FF;
letter-spacing: 2px;
text-align: center;
font-style: normal;
white-space: nowrap;
margin-top: 20px;
}
.mom {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: center;
font-style: normal;
margin-top: 20px;
}
}
.line {
width: 500px;
height: 205px;
background: #F9FCFF;
}
// .leftTitle {
// .item {
// width: 67px;
// height: 180px;
// padding: 37px 23px;
// background: #F9FCFF;
// font-family: PingFangSC, PingFang SC;
// font-weight: 400;
// font-size: 18px;
// color: #000000;
// line-height: 25px;
// letter-spacing: 1px;
// // text-align: left;
// font-style: normal;
// }
// }
</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> -->

View File

@@ -1,221 +0,0 @@
<template>
<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%;">
<!-- 销量模块直接传递整合了flag的salesData -->
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
<div style='position: relative;'>
<div class="title">
销量·
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[0].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[0].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
</div>
</div>
<!-- 单价模块直接传递整合了flag的unitPriceData -->
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
<div style='position: relative;'>
<div class="title">
单价·/
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[1].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[1].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
</div>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
export default {
name: 'ProductionStatus',
components: { Container, operatingSingleBar },
props: {
monthAnalysis: {
type: Array,
default: () => [
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
]
},
dateData: {
type: Object,
default: () => {}
},
title: {
type: String,
default: ''
},
factory: {
type: [String,Number],
default: ''
},
month: {
type: String,
default: ''
},
},
data() {
return {
chart: null,
// 初始化数据包含flag字段
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
}
},
watch: {
monthAnalysis: {
handler(newVal) {
this.updateChart(newVal)
},
deep: true,
immediate: true
}
},
mounted() {
this.updateChart(this.monthAnalysis)
},
methods: {
handleDashboardClick(path) {
this.$router.push({
path: path,
query: {
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
dateData: this.dateData
}
})
},
// 判断flag的核心方法
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;
},
updateChart(data) {
// 数据兜底
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
// 核心修改将flag整合到数据对象中无需单独定义salesFlag/unitPriceFlag
this.salesData = {
...salesItem, // 合并原有字段
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
};
this.unitPriceData = {
...unitPriceItem, // 合并原有字段
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
};
// 调试:确认整合后的数据
console.log('整合flag后的销量数据', this.salesData);
console.log('整合flag后的单价数据', this.unitPriceData);
}
}
}
</script>
<style lang='scss' scoped>
.scroll-container {
max-height: 210px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
.topItem-container {
display: flex;
justify-content: space-between;
}
.dashboard {
flex: 1;
min-width: 300px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
margin: 0 4px;
.title {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 2px;
text-align: left;
margin-bottom: 12px;
}
.chart-wrap {
width: 100%;
height: calc(100% - 30px);
}
.number {
display: flex;
align-items: center;
gap: 30px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
}
.mom {
width: 97px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
z-index: 1000;
}
}
.dashboard.left {
margin-left: 0;
}
.dashboard.right {
margin-right: 0;
}
</style>

View File

@@ -173,26 +173,62 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 40, barWidth: 40,
label: {
show: false
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
},
// 实际差值标签独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: {
show: false
},
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [32, 0], offset: [32, 0],
width: 100, width: 100,
height: 22, height: 22,
formatter: (params) => { formatter: () => {
const diff = data.diff || []; const diff = data.diff || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -225,45 +261,19 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 14, fontSize: 14,
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 14, fontSize: 14,
} }
} }
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
} }
] ]
}; };

View File

@@ -32,7 +32,6 @@
width: 1220px; width: 1220px;
background-color: rgba(249, 252, 255, 1); background-color: rgba(249, 252, 255, 1);
"> ">
<!-- <top-item /> -->
<operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" /> <operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" />
</div> </div>
</div> </div>
@@ -88,23 +87,6 @@ export default {
sortChange(value) { sortChange(value) {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断rate对应的flag值<1为0>1为1
* @param {number} rate 处理后的rate值已*100
* @returns {0|1} flag值
*/
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;
},
/** /**
* 核心处理函数:在所有数据都准备好后,才组装 chartData * 核心处理函数:在所有数据都准备好后,才组装 chartData
*/ */
@@ -118,7 +100,7 @@ getRateFlag(rate, real, target) {
const groupReal = [this.groupData.real]; // 实际值数组 const groupReal = [this.groupData.real]; // 实际值数组
const groupRate = [this.groupData.rate]; // 完成率数组 const groupRate = [this.groupData.rate]; // 完成率数组
// 新增集团rate对应的flag // 新增集团rate对应的flag
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; const groupFlag = [this.groupData.rate >= 100 ? 1 : 0];
console.log('集团数据数组:', { console.log('集团数据数组:', {
groupTarget, groupTarget,
@@ -139,7 +121,7 @@ getRateFlag(rate, real, target) {
const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryRate = this.factoryData.map(item => item.rate || 0);
const factoryDiff = this.factoryData.map(item => item.diff || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0);
// 新增每个工厂rate对应的flag数组 // 新增每个工厂rate对应的flag数组
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
// 3. 组装最终的chartData供子组件使用 // 3. 组装最终的chartData供子组件使用
this.chartData = { this.chartData = {

View File

@@ -32,7 +32,6 @@
width: 1220px; width: 1220px;
background-color: rgba(249, 252, 255, 1); background-color: rgba(249, 252, 255, 1);
"> ">
<!-- <top-item /> -->
<operatingBar :dateData="dateData" @sort-change="sortChange" :chartData="chartData" /> <operatingBar :dateData="dateData" @sort-change="sortChange" :chartData="chartData" />
</div> </div>
</div> </div>
@@ -83,23 +82,6 @@ export default {
sortChange(value) { sortChange(value) {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断rate对应的flag值<1为0>1为1
* @param {number} rate 处理后的rate值已*100
* @returns {0|1} flag值
*/
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;
},
/** /**
* 核心处理函数:在所有数据都准备好后,才组装 chartData * 核心处理函数:在所有数据都准备好后,才组装 chartData
@@ -114,7 +96,7 @@ getRateFlag(rate, real, target) {
const groupReal = [this.groupData.real]; // 实际值数组 const groupReal = [this.groupData.real]; // 实际值数组
const groupRate = [this.groupData.rate]; // 完成率数组 const groupRate = [this.groupData.rate]; // 完成率数组
// 新增集团rate对应的flag // 新增集团rate对应的flag
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; const groupFlag = [this.groupData.rate >= 100 ? 1 : 0];
console.log('集团数据数组:', { console.log('集团数据数组:', {
groupTarget, groupTarget,
@@ -135,7 +117,7 @@ getRateFlag(rate, real, target) {
const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryRate = this.factoryData.map(item => item.rate || 0);
const factoryDiff = this.factoryData.map(item => item.diff || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0);
// 新增每个工厂rate对应的flag数组 // 新增每个工厂rate对应的flag数组
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
// 3. 组装最终的chartData供子组件使用 // 3. 组装最终的chartData供子组件使用
this.chartData = { this.chartData = {

View File

@@ -1,245 +0,0 @@
<template>
<div class="lineBottom" style="height: 180px; width: 100%">
<operatingLineBarSaleSingle :refName=" 'totalOperating' " :chartData="chartD" style="height: 99%; width: 100%" />
</div>
</template>
<script>
import operatingLineBarSaleSingle from './operatingLineBarSaleSingle.vue';
import * as echarts from 'echarts';
export default {
name: "Container",
components: { operatingLineBarSaleSingle },
props: ["detailData"],
data() {
return {
};
},
computed: {
locations() {
return ['预算', '实际'];
},
chartD() {
// 背景图片路径(若不需要可注释)
// const bgImageUrl = require('@/assets/img/labelBg.png');
console.log('this.detailData', this.detailData);
const rate = this.detailData?.rate || 0
const diff = this.detailData?.diff || 0
console.log('diff', diff);
const seriesData = [
{
value: this.detailData?.budget || 0,
flag: 1, // 实际项:达标(绿色)
label: {
show: true,
position: 'top',
offset: [0, 0],
fontSize: 14,
},
itemStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
]
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
},
{
value: this.detailData?.real || 0,
flag: this.detailData?.flag, // 实际项:达标(绿色)
label: {
show: true,
position: 'top',
offset: [0, 0],
fontSize: 14,
},
itemStyle: {
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
}
];
const data = {
allPlaceNames: ['预算', '实际'],
series: [
{
type: 'bar',
barWidth: 60,
barCategoryGap: '50%',
data: seriesData,
itemStyle: {
color: (params) => {
const currentFlag = params.data.flag || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
}
},
},
],
};
console.log('data', data);
return data;
}
},
methods: {},
};
</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, PingFangSC;
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;
margin-right: 46px;
}
.legend {
display: flex;
gap: 16px;
align-items: center;
margin: 0;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFangSC;
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);
}
.button-group {
display: flex;
position: relative;
gap: 2px;
width: 283px;
align-items: center;
height: 24px;
background: #ecf4fe;
border-radius: 12px;
margin: 0;
.item-button {
cursor: pointer;
width: 142px;
height: 24px;
font-family: PingFangSC;
font-weight: 400;
font-size: 12px;
color: #0b58ff;
line-height: 24px;
text-align: center;
font-style: normal;
letter-spacing: 8px;
padding-left: 8px;
}
.item-button.active {
width: 142px;
height: 24px;
background: #3071ff;
border-radius: 12px;
color: #ffffff;
font-weight: 500;
}
}
}
}
</style>

View File

@@ -1,202 +0,0 @@
<template>
<div style="flex: 1">
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
<!-- 1. 移除 .kpi-content 的固定高度改为自适应 -->
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<!-- 新增topItem 专属包裹容器统一控制样式和布局 -->
<div class="topItem-container" style="display: flex; gap: 8px;">
<div class="dashboard">
<div class="title">
累计完成率
</div>
<div class="number">
<div class="yield">
{{ ytdData?.rate || 0}}%
</div>
<div class="mom">
同比{{ ytdData?.yoyRate || 0}}%
<img v-if="ytdData?.yoyRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div>
</div>
<!-- <div class="electricityGauge">
<electricityGauge :id=" 'totalG' " :detailData="ytdData" id="totalGauge"></electricityGauge>
</div> -->
</div>
<div class="line" style="padding: 0px;">
<verticalBarChart :refName=" 'totalVerticalBarChart' " :detailData="ytdData">
</verticalBarChart>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default {
name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart },
// mixins: [resize],
props: {
ytdData: { // 接收父组件传递的设备数据数组
type: Object,
default: () => {} // 默认空数组,避免报错
},
title: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
month: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
},
data() {
return {
chart: null,
}
},
watch: {
itemData: {
handler(newValue, oldValue) {
// this.updateChart()
},
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())
},
methods: {
}
}
</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;
}
.dashboard {
width: 264px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
.title {
// width: 190px;
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;
letter-spacing: 2px;
}
.number {
font-family: YouSheBiaoTiHei;
font-size: 46px;
color: #0B58FF;
letter-spacing: 2px;
text-align: center;
font-style: normal;
margin-top: 20px;
}
.mom {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: center;
font-style: normal;
margin-top: 20px;
}
}
.line {
width: 500px;
height: 205px;
background: #F9FCFF;
}
// .leftTitle {
// .item {
// width: 67px;
// height: 180px;
// padding: 37px 23px;
// background: #F9FCFF;
// font-family: PingFangSC, PingFang SC;
// font-weight: 400;
// font-size: 18px;
// color: #000000;
// line-height: 25px;
// letter-spacing: 1px;
// // text-align: left;
// font-style: normal;
// }
// }
</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> -->

View File

@@ -1,226 +0,0 @@
<template>
<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:detailData.flags>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';
export default {
components: {},
data() {
return {
myChart: null // 存储图表实例,避免重复创建
};
},
props: {
// 明确接收的props结构增强可读性
refName: {
type: String,
default: () => 'verticalBarChart',
},
detailData: {
type: Object,
default: () => ({
}),
}
},
mounted() {
this.$nextTick(() => {
this.updateChart();
});
},
// 新增:监听 chartData 变化
watch: {
// 深度监听数据变化,仅更新图表配置(不销毁实例)
detailData: {
handler() {
console.log(this.chartData, 'chartData');
this.updateChart();
},
deep: true,
immediate: true // 初始化时立即执行
}
},
methods: {
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;
},
updateChart() {
const chartDom = this.$refs[this.refName];
if (!chartDom) {
console.error('图表容器未找到!');
return;
}
if (this.myChart) {
this.myChart.dispose();
}
this.myChart = echarts.init(chartDom);
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
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
},
// formatter: (params) => {
// let html = `${params[0].axisValue}<br/>`;
// params.forEach(item => {
// const unit = item.seriesName === '完成率' ? '%' : (
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
// );
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
// });
// return html;
// }
},
grid: {
top: 40,
bottom: 15,
right: 80,
left: 10,
containLabel: true,
show: false // 隐藏grid背景避免干扰
},
xAxis: {
// 横向柱状图的x轴必须设为数值轴否则无法正常展示数值
type: 'value',
// offset: 0,
// boundaryGap: true ,
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
axisTick: { show: false },
min: 0,
//
splitNumber: 4,
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: xData // 数值轴不需要手动设置data由series的数据自动生成
},
yAxis: {
type: 'category',
axisLabel: {
color: 'rgba(0, 0, 0, 0.75)',
fontSize: 12,
interval: 0,
padding: [5, 0, 0, 0]
},
axisLine: {
show: true, // 显示Y轴轴线关键
lineStyle: {
color: '#E5E6EB', // 轴线颜色(浅灰色,可自定义)
width: 1, // 轴线宽度
type: 'solid' // 实线可选dashed虚线、dotted点线
}
},
axisTick: { show: false },
// padding: [300, 100, 100, 100],
data: ['实际', '预算'] // y轴分类实际、预算
},
series: [
{
// name: '预算',
type: 'bar',
barWidth: 24,
// barCategoryGap: '50', // 柱子之间的间距(相对于柱子宽度)
// 数据长度与yAxis的分类数量匹配实际、预算各一个值
data: [{
value: this.detailData.real,
label: {
show: true,
position: 'right',
fontSize: 14,
},
itemStyle: {
color: flagValue === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
},
borderRadius: [4, 4, 0, 0]
}
}, {
value: this.detailData.target,
label: {
show: true,
position: 'right',
fontSize: 14,
},
itemStyle: {
// 预算的渐变颜色(蓝系渐变)
color: {
type: 'linear',
x: 1, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: '#82CCFF' }, // 浅蓝
{ offset: 1, color: '#4B9DFF' } // 深蓝
]
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
},],
},
]
};
option && this.myChart.setOption(option);
// 窗口缩放适配和销毁逻辑保持不变
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();
});
}
},
};
</script>

View File

@@ -1,217 +0,0 @@
<template>
<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%;">
<!-- 销量模块直接传递整合了flag的salesData -->
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
<div style='position: relative;'>
<div class="title">
销量·
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[0].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[0].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
</div>
</div>
<!-- 单价模块直接传递整合了flag的unitPriceData -->
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
<div style='position: relative;'>
<div class="title">
单价·/
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[1].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[1].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
</div>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
export default {
name: 'ProductionStatus',
components: { Container, operatingSingleBar },
props: {
ytdAnalysis: {
type: Array,
default: () => [
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
]
},
dateData: {
type: Object,
default: () => {}
},
title: {
type: String,
default: ''
},
month: {
type: String,
default: ''
},
},
data() {
return {
chart: null,
// 初始化数据包含flag字段
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
}
},
watch: {
ytdAnalysis: {
handler(newVal) {
this.updateChart(newVal)
},
deep: true,
immediate: true
}
},
mounted() {
this.updateChart(this.ytdAnalysis)
},
methods: {
handleDashboardClick(path) {
this.$router.push({
path: path,
query: {
factory: this.$route.query.factory ? this.$route.query.factory : 5,
dateData: this.dateData
}
})
},
// 判断flag的核心方法
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;
},
updateChart(data) {
// 数据兜底
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
// 核心修改将flag整合到数据对象中无需单独定义salesFlag/unitPriceFlag
this.salesData = {
...salesItem, // 合并原有字段
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
};
this.unitPriceData = {
...unitPriceItem, // 合并原有字段
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
};
// 调试:确认整合后的数据
console.log('整合flag后的销量数据', this.salesData);
console.log('整合flag后的单价数据', this.unitPriceData);
}
}
}
</script>
<style lang='scss' scoped>
.scroll-container {
max-height: 210px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
.topItem-container {
display: flex;
justify-content: space-between;
}
.dashboard {
flex: 1;
min-width: 300px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
margin: 0 4px;
.title {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 2px;
text-align: left;
margin-bottom: 12px;
}
.chart-wrap {
width: 100%;
height: calc(100% - 30px);
}
.number {
display: flex;
align-items: center;
gap: 30px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
}
.mom {
width: 97px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
z-index: 1000;
}
}
.dashboard.left {
margin-left: 0;
}
.dashboard.right {
margin-right: 0;
}
</style>

View File

@@ -128,10 +128,6 @@ export default {
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
}, },
methods: { methods: {
// sortChange(value) {
// this.sort = value
// this.getData()
// },
getData() { getData() {
getDepreciationAnalysisData({ getDepreciationAnalysisData({
startTime: this.dateData.startTime, startTime: this.dateData.startTime,
@@ -144,12 +140,6 @@ export default {
console.log(res); console.log(res);
this.monthData= res.data.month this.monthData= res.data.month
this.ytdData = res.data.ytd this.ytdData = res.data.ytd
// this.saleData = res.data.SaleData
// this.premiumProduct = res.data.premiumProduct
// this.salesTrendMap = res.data.salesTrendMap
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
}) })
}, },
handleTimeChange(obj) { handleTimeChange(obj) {
@@ -199,28 +189,7 @@ export default {
return false; return false;
} }
screenfull.toggle(this.$refs.dayReportB); screenfull.toggle(this.$refs.dayReportB);
}, }
// 导出
// 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> </script>

View File

@@ -1,298 +0,0 @@
<template>
<div style="flex: 1">
<Container name="数据趋势" icon="cockpitItemIcon" size="opLargeBg" topSize="large">
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%; gap: 16px">
<div class="right" style="
height: 191px;
display: flex;
width: 1595px;
background-color: rgba(249, 252, 255, 1);
">
<dataTrendBar @changeItem="handleChange" :chartData="chartData" />
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from "../components/container.vue";
import dataTrendBar from "./dataTrendBar.vue";
export default {
name: "ProductionStatus",
components: { Container, dataTrendBar },
props: {
trend: {
type: Array,
// 默认值与实际数据结构一致12个月
default: () => [
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
]
},
},
data() {
return {
chartData: {
months: [], // 月份数组2025年01月 - 2025年12月
rates: [], // 每月完成率(百分比)
reals: [], // 每月实际值
budgets: [],// 每月预算值
diffs: [], // 每月差值
flags: [] // 每月达标标识≥100 → 1<100 → 0
}
};
},
watch: {
trend: {
handler(newVal) {
this.processTrendData(newVal);
},
immediate: true,
deep: true,
},
},
mounted() {
this.processTrendData(this.trend);
},
methods: {
handleChange(value) {
this.$emit("handleChange", value);
},
/**
* 处理趋势数据适配12个月的数组结构
* @param {Array} trendData - 原始趋势数组12个月
*/
processTrendData(trendData) {
// 数据兜底确保是数组且长度为12
const validTrend = Array.isArray(trendData)
? trendData
: []
// 初始化空数组
const months = [];
const rates = [];
const reals = [];
const budgets = [];
const diffs = [];
const flags = [];
// 遍历12个月数据
validTrend.forEach(item => {
// 基础数据提取(兜底处理)
const month = item.title ?? '';
const budget = Number(item.budget) || 0;
const real = Number(item.real) || 0;
const rate = Number(item.rate) || 0;
const diff = Number(item.diff) || 0;
// 计算达标标识≥100 → 1<100 → 0
const flag = this.getRateFlag(rate, real, budget);
// 填充数组
months.push(month);
rates.push(rate); // 转为百分比并取整
reals.push(real);
budgets.push(budget);
diffs.push(diff);
flags.push(flag);
});
// 更新chartData响应式
this.chartData = {
months,
rates,
reals,
budgets,
diffs,
flags
};
console.log('处理后的趋势数据:', this.chartData);
},
/**
* 计算达标标识
* @param {Number} rate - 完成率原始值如1.2 → 120%
* @returns {Number} 1: 达标≥100%0: 未达标(<100%
*/
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;
},
},
};
</script>
<style lang="scss" scoped>
/* 滚动容器样式 */
.scroll-container {
max-height: 210px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-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 样式 */
.production-status-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.production-status-chart-tooltip * {
color: #fff !important;
}
</style>

View File

@@ -1,477 +0,0 @@
<template>
<div class="coreBar">
<!-- 新增行容器包裹各基地情况和barTop -->
<div class="header-row">
<div class="barTop">
<!-- 关键新增右侧容器包裹图例和按钮组实现整体靠右 -->
<div class="right-container">
<div class="legend">
<span class="legend-item">
<span class="legend-icon line yield"></span>
完成率
</span>
<span class="legend-item">
<span class="legend-icon square target"></span>
预算
</span>
<span class="legend-item">
<span class="legend-icon square achieved"></span>
实际·达标
</span>
<span class="legend-item">
<span class="legend-icon square unachieved"></span>
实际·未达标
</span>
</div>
<div class="button-group">
<div class="item-button category-btn">
<span class="item-text">类目选择</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="dropdown-arrow" :class="{ 'rotate': isDropdownShow }"></span>
</div>
<div class="dropdown-options" v-if="isDropdownShow">
<div class="dropdown-option" v-for="(item, index) in profitOptions" :key="index"
@click.stop="selectProfit(item)">
{{ item }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="lineBottom" style="height: 100%; width: 100%">
<operatingLineBar :chartData="chartD" style="height: 99%; width: 100%" />
</div>
</div>
</template>
<script>
import operatingLineBar from './operatingLineBarSale.vue';
import * as echarts from 'echarts';
export default {
name: "Container",
components: { operatingLineBar },
props: ["chartData"],
data() {
return {
activeButton: 0,
isDropdownShow: false,
selectedProfit: '营业收入', // 选中的名称初始为null
profitOptions: [
'营业收入',
'单价',
'销量',
]
};
},
computed: {
// profitOptions() {
// return this.categoryData.map(item => item.name) || [];
// },
currentDataSource() {
console.log('yyyy', this.chartData);
return this.chartData
},
locations() {
console.log('this.chartData', this.chartData);
return this.chartData.months
},
// 根据按钮切换生成对应的 chartData
chartD() {
const data = this.currentDataSource;
console.log(this.currentDataSource, 'currentDataSource');
const salesData = {
allPlaceNames: this.locations,
series: [
// 1. 完成率(折线图)
{
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(40, 138, 255, 0)' }
])
},
data: data.rates, // 完成率(%
symbol: 'circle',
symbolSize: 6
},
// 2. 目标(柱状图)
{
name: '预算',
type: 'bar',
yAxisIndex: 0, // 左侧Y轴万元
barWidth: 14,
itemStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
]
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.budgets // 目标销量(万元)
},
// 3. 实际(柱状图,含达标状态)
{
name: '实际',
type: 'bar',
yAxisIndex: 0,
barWidth: 14,
label: {
show: true,
position: 'top',
offset: [0, 0],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: (params) => {
const diff = data.diffs || [];
const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0;
const currentFlag = flags[params.dataIndex] || 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: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)
const safeFlag = data.flags;
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals // 实际销量(万元)
}
]
};
// 根据按钮状态返回对应数据
return salesData;
}
},
methods: {
selectProfit(item) {
this.selectedProfit = item;
this.isDropdownShow = false;
this.$emit("changeItem", item);
}
},
};
</script>
<style scoped lang="scss">
.coreBar {
display: flex;
flex-direction: column;
width: 100%;
padding: 12px;
// 新增:头部行容器,实现一行排列
.header-row {
display: flex;
justify-content: flex-end; // 左右两端对齐
align-items: center; // 垂直居中
// width: 100%;
margin-bottom: 8px; // 与下方图表区保留间距(可根据需求调整)
}
// 各基地情况标题样式
.base-title {
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
font-style: normal;
padding: 0 0 0 16px; // 保留原有内边距
white-space: nowrap; // 防止文字换行
}
.barTop {
// 移除原有flex和justify-content由header-row控制
width: auto; // 自适应宽度
// 保留原有align-items确保内部元素垂直居中
align-items: center;
gap: 16px;
// 1. 右侧容器:包裹图例和按钮组,整体靠右
.right-container {
display: flex;
align-items: center; // 图例和按钮组垂直居中
gap: 24px; // 图例与按钮组的间距,避免贴紧
margin-right: 46px; // 右侧整体留边,与原按钮组边距一致
}
// 2. 图例:在右侧容器内横向排列
.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);
}
// 3. 按钮组:在右侧容器内,保留原有样式
.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;
overflow: hidden;
.item-text {
display: inline-block;
}
}
.category-btn {
width: 75px;
border-top-left-radius: 12px;
border-bottom-left-radius: 12px;
background: #ffffff;
color: #0b58ff;
text-align: center;
}
.profit-btn {
width: 123px;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
position: relative;
padding: 0 18px 0 8px;
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: 123px;
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>

View File

@@ -1,204 +0,0 @@
<template>
<div style="flex: 1">
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
<!-- 1. 移除 .kpi-content 的固定高度改为自适应 -->
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<!-- 新增topItem 专属包裹容器统一控制样式和布局 -->
<div class="topItem-container" style="display: flex; gap: 8px;">
<div class="dashboard">
<div class="title">
{{ month }}月完成率
</div>
<div class="number">
<div class="yield">
{{ monthData?.rate || 0 }}%
</div>
<div class="mom">
环比{{ monthData?.momRate }}%
<img v-if="monthData?.momRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div>
</div>
<!-- <div class="electricityGauge">
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
</div> -->
</div>
<div class="line" style="padding: 0px;">
<verticalBarChart :detailData="monthData">
</verticalBarChart>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default {
name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart },
// mixins: [resize],
props: {
monthData: { // 接收父组件传递的设备数据数组
type: Object,
default: () => {} // 默认空数组,避免报错
},
title: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
month: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
},
data() {
return {
chart: null,
}
},
watch: {
// itemData: {
// handler(newValue, oldValue) {
// // this.updateChart()
// },
// 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())
},
methods: {
}
}
</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;
}
.dashboard {
width: 264px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
.title {
// width: 190px;
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;
letter-spacing: 2px;
}
.number {
font-family: YouSheBiaoTiHei;
font-size: 46px;
color: #0B58FF;
letter-spacing: 2px;
text-align: center;
font-style: normal;
white-space: nowrap;
margin-top: 20px;
}
.mom {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: center;
font-style: normal;
margin-top: 20px;
}
}
.line {
width: 500px;
height: 205px;
background: #F9FCFF;
}
// .leftTitle {
// .item {
// width: 67px;
// height: 180px;
// padding: 37px 23px;
// background: #F9FCFF;
// font-family: PingFangSC, PingFang SC;
// font-weight: 400;
// font-size: 18px;
// color: #000000;
// line-height: 25px;
// letter-spacing: 1px;
// // text-align: left;
// font-style: normal;
// }
// }
</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> -->

View File

@@ -1,221 +0,0 @@
<template>
<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%;">
<!-- 销量模块直接传递整合了flag的salesData -->
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
<div style='position: relative;'>
<div class="title">
销量·
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[0].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[0].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
</div>
</div>
<!-- 单价模块直接传递整合了flag的unitPriceData -->
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
<div style='position: relative;'>
<div class="title">
单价·/
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[1].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[1].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
</div>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
export default {
name: 'ProductionStatus',
components: { Container, operatingSingleBar },
props: {
monthAnalysis: {
type: Array,
default: () => [
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
]
},
dateData: {
type: Object,
default: () => {}
},
title: {
type: String,
default: ''
},
factory: {
type: [String,Number],
default: ''
},
month: {
type: String,
default: ''
},
},
data() {
return {
chart: null,
// 初始化数据包含flag字段
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
}
},
watch: {
monthAnalysis: {
handler(newVal) {
this.updateChart(newVal)
},
deep: true,
immediate: true
}
},
mounted() {
this.updateChart(this.monthAnalysis)
},
methods: {
handleDashboardClick(path) {
this.$router.push({
path: path,
query: {
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
dateData: this.dateData
}
})
},
// 判断flag的核心方法
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;
},
updateChart(data) {
// 数据兜底
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
// 核心修改将flag整合到数据对象中无需单独定义salesFlag/unitPriceFlag
this.salesData = {
...salesItem, // 合并原有字段
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
};
this.unitPriceData = {
...unitPriceItem, // 合并原有字段
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
};
// 调试:确认整合后的数据
console.log('整合flag后的销量数据', this.salesData);
console.log('整合flag后的单价数据', this.unitPriceData);
}
}
}
</script>
<style lang='scss' scoped>
.scroll-container {
max-height: 210px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
.topItem-container {
display: flex;
justify-content: space-between;
}
.dashboard {
flex: 1;
min-width: 300px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
margin: 0 4px;
.title {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 2px;
text-align: left;
margin-bottom: 12px;
}
.chart-wrap {
width: 100%;
height: calc(100% - 30px);
}
.number {
display: flex;
align-items: center;
gap: 30px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
}
.mom {
width: 97px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
z-index: 1000;
}
}
.dashboard.left {
margin-left: 0;
}
.dashboard.right {
margin-right: 0;
}
</style>

View File

@@ -173,26 +173,62 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 40, barWidth: 40,
label: {
show: false
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
},
// 实际差值标签独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: {
show: false
},
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [32, 0], offset: [32, 0],
width: 100, width: 100,
height: 22, height: 22,
formatter: (params) => { formatter: () => {
const diff = data.diff || []; const diff = data.diff || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -225,45 +261,19 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 14, fontSize: 14,
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 14, fontSize: 14,
} }
} }
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
} }
] ]
}; };

View File

@@ -32,7 +32,6 @@
width: 1220px; width: 1220px;
background-color: rgba(249, 252, 255, 1); background-color: rgba(249, 252, 255, 1);
"> ">
<!-- <top-item /> -->
<operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" /> <operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" />
</div> </div>
</div> </div>
@@ -88,23 +87,6 @@ export default {
sortChange(value) { sortChange(value) {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断rate对应的flag值<1为0>1为1
* @param {number} rate 处理后的rate值已*100
* @returns {0|1} flag值
*/
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;
},
/** /**
* 核心处理函数:在所有数据都准备好后,才组装 chartData * 核心处理函数:在所有数据都准备好后,才组装 chartData
*/ */
@@ -118,7 +100,7 @@ getRateFlag(rate, real, target) {
const groupReal = [this.groupData.real]; // 实际值数组 const groupReal = [this.groupData.real]; // 实际值数组
const groupRate = [this.groupData.rate]; // 完成率数组 const groupRate = [this.groupData.rate]; // 完成率数组
// 新增集团rate对应的flag // 新增集团rate对应的flag
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; const groupFlag = [this.groupData.rate];
console.log('集团数据数组:', { console.log('集团数据数组:', {
groupTarget, groupTarget,
@@ -139,7 +121,7 @@ getRateFlag(rate, real, target) {
const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryRate = this.factoryData.map(item => item.rate || 0);
const factoryDiff = this.factoryData.map(item => item.diff || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0);
// 新增每个工厂rate对应的flag数组 // 新增每个工厂rate对应的flag数组
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
// 3. 组装最终的chartData供子组件使用 // 3. 组装最终的chartData供子组件使用
this.chartData = { this.chartData = {

View File

@@ -1,202 +0,0 @@
<template>
<div style="flex: 1">
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
<!-- 1. 移除 .kpi-content 的固定高度改为自适应 -->
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<!-- 新增topItem 专属包裹容器统一控制样式和布局 -->
<div class="topItem-container" style="display: flex; gap: 8px;">
<div class="dashboard">
<div class="title">
累计完成率
</div>
<div class="number">
<div class="yield">
{{ ytdData?.rate || 0}}%
</div>
<div class="mom">
同比{{ ytdData?.yoyRate || 0}}%
<img v-if="ytdData?.yoyRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div>
</div>
<!-- <div class="electricityGauge">
<electricityGauge :id=" 'totalG' " :detailData="ytdData" id="totalGauge"></electricityGauge>
</div> -->
</div>
<div class="line" style="padding: 0px;">
<verticalBarChart :refName=" 'totalVerticalBarChart' " :detailData="ytdData">
</verticalBarChart>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default {
name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart },
// mixins: [resize],
props: {
ytdData: { // 接收父组件传递的设备数据数组
type: Object,
default: () => {} // 默认空数组,避免报错
},
title: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
month: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
},
data() {
return {
chart: null,
}
},
watch: {
itemData: {
handler(newValue, oldValue) {
// this.updateChart()
},
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())
},
methods: {
}
}
</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;
}
.dashboard {
width: 264px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
.title {
// width: 190px;
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;
letter-spacing: 2px;
}
.number {
font-family: YouSheBiaoTiHei;
font-size: 46px;
color: #0B58FF;
letter-spacing: 2px;
text-align: center;
font-style: normal;
margin-top: 20px;
}
.mom {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: center;
font-style: normal;
margin-top: 20px;
}
}
.line {
width: 500px;
height: 205px;
background: #F9FCFF;
}
// .leftTitle {
// .item {
// width: 67px;
// height: 180px;
// padding: 37px 23px;
// background: #F9FCFF;
// font-family: PingFangSC, PingFang SC;
// font-weight: 400;
// font-size: 18px;
// color: #000000;
// line-height: 25px;
// letter-spacing: 1px;
// // text-align: left;
// font-style: normal;
// }
// }
</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> -->

View File

@@ -1,226 +0,0 @@
<template>
<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:detailData.flags>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';
export default {
components: {},
data() {
return {
myChart: null // 存储图表实例,避免重复创建
};
},
props: {
// 明确接收的props结构增强可读性
refName: {
type: String,
default: () => 'verticalBarChart',
},
detailData: {
type: Object,
default: () => ({
}),
}
},
mounted() {
this.$nextTick(() => {
this.updateChart();
});
},
// 新增:监听 chartData 变化
watch: {
// 深度监听数据变化,仅更新图表配置(不销毁实例)
detailData: {
handler() {
console.log(this.chartData, 'chartData');
this.updateChart();
},
deep: true,
immediate: true // 初始化时立即执行
}
},
methods: {
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;
},
updateChart() {
const chartDom = this.$refs[this.refName];
if (!chartDom) {
console.error('图表容器未找到!');
return;
}
if (this.myChart) {
this.myChart.dispose();
}
this.myChart = echarts.init(chartDom);
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
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
},
// formatter: (params) => {
// let html = `${params[0].axisValue}<br/>`;
// params.forEach(item => {
// const unit = item.seriesName === '完成率' ? '%' : (
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
// );
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
// });
// return html;
// }
},
grid: {
top: 40,
bottom: 15,
right: 80,
left: 10,
containLabel: true,
show: false // 隐藏grid背景避免干扰
},
xAxis: {
// 横向柱状图的x轴必须设为数值轴否则无法正常展示数值
type: 'value',
// offset: 0,
// boundaryGap: true ,
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
axisTick: { show: false },
min: 0,
//
splitNumber: 4,
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: xData // 数值轴不需要手动设置data由series的数据自动生成
},
yAxis: {
type: 'category',
axisLabel: {
color: 'rgba(0, 0, 0, 0.75)',
fontSize: 12,
interval: 0,
padding: [5, 0, 0, 0]
},
axisLine: {
show: true, // 显示Y轴轴线关键
lineStyle: {
color: '#E5E6EB', // 轴线颜色(浅灰色,可自定义)
width: 1, // 轴线宽度
type: 'solid' // 实线可选dashed虚线、dotted点线
}
},
axisTick: { show: false },
// padding: [300, 100, 100, 100],
data: ['实际', '预算'] // y轴分类实际、预算
},
series: [
{
// name: '预算',
type: 'bar',
barWidth: 24,
// barCategoryGap: '50', // 柱子之间的间距(相对于柱子宽度)
// 数据长度与yAxis的分类数量匹配实际、预算各一个值
data: [{
value: this.detailData.real,
label: {
show: true,
position: 'right',
fontSize: 14,
},
itemStyle: {
color: flagValue === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
},
borderRadius: [4, 4, 0, 0]
}
}, {
value: this.detailData.target,
label: {
show: true,
position: 'right',
fontSize: 14,
},
itemStyle: {
// 预算的渐变颜色(蓝系渐变)
color: {
type: 'linear',
x: 1, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: '#82CCFF' }, // 浅蓝
{ offset: 1, color: '#4B9DFF' } // 深蓝
]
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
},],
},
]
};
option && this.myChart.setOption(option);
// 窗口缩放适配和销毁逻辑保持不变
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();
});
}
},
};
</script>

View File

@@ -1,217 +0,0 @@
<template>
<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%;">
<!-- 销量模块直接传递整合了flag的salesData -->
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
<div style='position: relative;'>
<div class="title">
销量·
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[0].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[0].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
</div>
</div>
<!-- 单价模块直接传递整合了flag的unitPriceData -->
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
<div style='position: relative;'>
<div class="title">
单价·/
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[1].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[1].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
</div>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
export default {
name: 'ProductionStatus',
components: { Container, operatingSingleBar },
props: {
ytdAnalysis: {
type: Array,
default: () => [
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
]
},
dateData: {
type: Object,
default: () => {}
},
title: {
type: String,
default: ''
},
month: {
type: String,
default: ''
},
},
data() {
return {
chart: null,
// 初始化数据包含flag字段
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
}
},
watch: {
ytdAnalysis: {
handler(newVal) {
this.updateChart(newVal)
},
deep: true,
immediate: true
}
},
mounted() {
this.updateChart(this.ytdAnalysis)
},
methods: {
handleDashboardClick(path) {
this.$router.push({
path: path,
query: {
factory: this.$route.query.factory ? this.$route.query.factory : 5,
dateData: this.dateData
}
})
},
// 判断flag的核心方法
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;
},
updateChart(data) {
// 数据兜底
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
// 核心修改将flag整合到数据对象中无需单独定义salesFlag/unitPriceFlag
this.salesData = {
...salesItem, // 合并原有字段
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
};
this.unitPriceData = {
...unitPriceItem, // 合并原有字段
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
};
// 调试:确认整合后的数据
console.log('整合flag后的销量数据', this.salesData);
console.log('整合flag后的单价数据', this.unitPriceData);
}
}
}
</script>
<style lang='scss' scoped>
.scroll-container {
max-height: 210px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
.topItem-container {
display: flex;
justify-content: space-between;
}
.dashboard {
flex: 1;
min-width: 300px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
margin: 0 4px;
.title {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 2px;
text-align: left;
margin-bottom: 12px;
}
.chart-wrap {
width: 100%;
height: calc(100% - 30px);
}
.number {
display: flex;
align-items: center;
gap: 30px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
}
.mom {
width: 97px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
z-index: 1000;
}
}
.dashboard.left {
margin-left: 0;
}
.dashboard.right {
margin-right: 0;
}
</style>

View File

@@ -128,10 +128,6 @@ export default {
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
}, },
methods: { methods: {
// sortChange(value) {
// this.sort = value
// this.getData()
// },
getData() { getData() {
getElectricityCostAnalysisData({ getElectricityCostAnalysisData({
startTime: this.dateData.startTime, startTime: this.dateData.startTime,
@@ -191,27 +187,6 @@ export default {
} }
screenfull.toggle(this.$refs.dayReportB); screenfull.toggle(this.$refs.dayReportB);
}, },
// 导出
// 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> </script>

View File

@@ -52,14 +52,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
@@ -69,20 +61,11 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue"; import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../electricityCostAnalysisComponents/monthlyOverview.vue"; import monthlyOverview from "../electricityCostAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../electricityCostAnalysisComponents/totalOverview.vue"; import totalOverview from "../electricityCostAnalysisComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
import relatedIndicatorsAnalysis from "../electricityCostAnalysisComponents/relatedIndicatorsAnalysis.vue"; import relatedIndicatorsAnalysis from "../electricityCostAnalysisComponents/relatedIndicatorsAnalysis.vue";
import dataTrend from "../electricityCostAnalysisComponents/dataTrend.vue"; import dataTrend from "../electricityCostAnalysisComponents/dataTrend.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import { getElectricityCostAnalysisFData } from '@/api/cockpit' import { getElectricityCostAnalysisFData } from '@/api/cockpit'
// import PSDO from "./components/PSDO.vue";
// import psiLineChart from "./components/psiLineChart.vue";
// import coreBottomLeft from "./components/coreBottomLeft.vue";
// import orderProgress from "./components/orderProgress.vue";
// import keyWork from "./components/keyWork.vue";
import moment from "moment"; import moment from "moment";
// import html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default { export default {
name: "electricityCostAnalysisBase", name: "electricityCostAnalysisBase",
components: { components: {
@@ -108,8 +91,7 @@ export default {
totalData: {}, totalData: {},
trend: [], trend: [],
relatedData: {}, relatedData: {},
trendName: '原片电费', trendName: '原片电费'
// cusProData: {},
}; };
}, },
@@ -275,35 +257,12 @@ export default {
}, },
changeDate(val) { changeDate(val) {
this.date = val; this.date = val;
// this.weekDay = this.weekArr[moment(this.date).format('e')]
// this.getData()
if (this.date === moment().format("yyyy-MM-DD")) { if (this.date === moment().format("yyyy-MM-DD")) {
this.loopTime(); this.loopTime();
} else { } else {
clearInterval(this.timer); clearInterval(this.timer);
} }
}, }
// 导出
// () {
// 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> </script>

View File

@@ -30,11 +30,8 @@ export default {
}, },
}, },
data() { data() {
return { return {};
// 移除:原 chartData 定义,改为计算属性
};
}, },
// 移除:原 watch 监听配置,计算属性自动响应 trendData 变化
computed: { computed: {
/** /**
* chartData 计算属性:自动响应 trendData 变化,格式化并提取各字段数组 * chartData 计算属性:自动响应 trendData 变化,格式化并提取各字段数组
@@ -58,7 +55,7 @@ export default {
diffValueArr.push(item.diff ?? 0); diffValueArr.push(item.diff ?? 0);
targetValueArr.push(item.budget ?? 0); targetValueArr.push(item.budget ?? 0);
proportionArr.push(item.rate ?? 0); proportionArr.push(item.rate ?? 0);
completedArr.push(item.rate && item.rate>=100 ? 1 : 0); completedArr.push(item.rate>=100 ? 1 : 0);
}); });
// 组装并返回格式化后的数据(结构与原一致) // 组装并返回格式化后的数据(结构与原一致)

View File

@@ -142,28 +142,63 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 14, barWidth: 14,
label: {
show: false
},
itemStyle: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)
const safeFlag = data.completed || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.value || [] // 实际销量(万元)
},
// 4. 实际差值标签独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: {
show: false
},
data: (data.value || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [0, 0], offset: [0, 0],
// 固定label尺寸68px×20px
width: 68, width: 68,
height: 20, height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸 formatter: () => {
formatter: (params) => {
const diff = data.diffValue || []; const diff = data.diffValue || [];
const flags = data.completed || []; const flags = data.completed || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -197,48 +232,21 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 11, fontSize: 11,
lineHeight: 20 lineHeight: 20
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 11, fontSize: 11,
lineHeight: 20 lineHeight: 20
} }
} }
},
itemStyle: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)
const safeFlag = data.completed || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.value || [] // 实际销量(万元)
} }
] ]
}; };

View File

@@ -17,9 +17,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降"> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge id="month" :detailData="factoryData"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<!-- 传递包含flag的factoryData给柱状图组件 --> <!-- 传递包含flag的factoryData给柱状图组件 -->
@@ -84,12 +81,7 @@ export default {
return 0; return 0;
} }
return value; return value;
}, }
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如89代表89%
* @returns {0|1} flag值
*/
} }
} }
</script> </script>

View File

@@ -174,26 +174,62 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 40, barWidth: 40,
label: {
show: false
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
},
// 实际差值标签独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: {
show: false
},
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [32, 0], offset: [32, 0],
width: 100, width: 100,
height: 22, height: 22,
formatter: (params) => { formatter: () => {
const diff = data.diff || []; const diff = data.diff || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -226,45 +262,19 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 14 fontSize: 14
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 14 fontSize: 14
} }
} }
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
} }
] ]
}; };

View File

@@ -101,21 +101,6 @@ export default {
}, },
}, },
methods: { methods: {
/**
* 核心方法按levelId匹配地名生成locations
*/
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;
},
processChartData() { processChartData() {
// 初始化空数据结构 // 初始化空数据结构
const initTopBarData = { const initTopBarData = {
@@ -132,7 +117,7 @@ export default {
topBarData.targets.push(level1Data.target || 0); topBarData.targets.push(level1Data.target || 0);
topBarData.reals.push(level1Data.real || 0); topBarData.reals.push(level1Data.real || 0);
topBarData.rate.push(level1Data.rate || 0); topBarData.rate.push(level1Data.rate || 0);
topBarData.flags.push(this.getRateFlag(level1Data.rate, level1Data.real, level1Data.target)); topBarData.flags.push(level1Data.rate >= 100 ? 1 : 0);
// 2. 处理levelId≠1的整合数据核心levelId匹配地名 // 2. 处理levelId≠1的整合数据核心levelId匹配地名
const barData = { ...initBarData }; const barData = { ...initBarData };
// 筛选有效数据levelId≠1 且 levelId在baseIndexToNameMap中 // 筛选有效数据levelId≠1 且 levelId在baseIndexToNameMap中
@@ -145,7 +130,7 @@ export default {
barData.targets.push(item.target || 0); barData.targets.push(item.target || 0);
barData.reals.push(item.real || 0); barData.reals.push(item.real || 0);
barData.rate.push(item.rate || 0); barData.rate.push(item.rate || 0);
barData.flags.push(this.getRateFlag(item.rate, item.real, item.target)); barData.flags.push(item.rate >= 100 ? 1 : 0);
}); });
// 3. 更新chartData // 3. 更新chartData

View File

@@ -101,21 +101,6 @@ export default {
}, },
}, },
methods: { methods: {
/**
* 核心方法按levelId匹配地名生成locations
*/
getRateFlag(rate, real, budget) {
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 1. 完成率 >= 100 => 达标
if (rate >= 100) return 1;
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
// if (rate === 0 && budget === 0) return 1;
// 其他情况 => 未达标
return 0;
},
processChartData() { processChartData() {
// 初始化空数据结构 // 初始化空数据结构
const initTopBarData = { const initTopBarData = {
@@ -132,7 +117,7 @@ export default {
topBarData.targets.push(level1Data.target || 0); topBarData.targets.push(level1Data.target || 0);
topBarData.reals.push(level1Data.real || 0); topBarData.reals.push(level1Data.real || 0);
topBarData.rate.push(level1Data.rate || 0); topBarData.rate.push(level1Data.rate || 0);
topBarData.flags.push(this.getRateFlag(level1Data.rate, level1Data.real, level1Data.target)); topBarData.flags.push(level1Data.rate >= 100 ? 1 : 0);
// 2. 处理levelId≠1的整合数据核心levelId匹配地名 // 2. 处理levelId≠1的整合数据核心levelId匹配地名
const barData = { ...initBarData }; const barData = { ...initBarData };
@@ -146,7 +131,7 @@ export default {
barData.targets.push(item.budget || 0); barData.targets.push(item.budget || 0);
barData.reals.push(item.real || 0); barData.reals.push(item.real || 0);
barData.rate.push(item.rate || 0); barData.rate.push(item.rate || 0);
barData.flags.push(this.getRateFlag(item.rate, item.real, item.budget)); barData.flags.push(item.rate >= 100 ? 1 : 0);
}); });
// 3. 更新chartData // 3. 更新chartData

View File

@@ -28,9 +28,6 @@
import Container from '../components/container.vue' import Container from '../components/container.vue'
import operatingSingleBar from './operatingSingleBar.vue' import operatingSingleBar from './operatingSingleBar.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, operatingSingleBar }, components: { Container, operatingSingleBar },

View File

@@ -19,9 +19,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降"> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge id="year" :detailData="factoryData"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<!-- 传递包含flag的factoryData给柱状图组件 --> <!-- 传递包含flag的factoryData给柱状图组件 -->
@@ -37,9 +34,6 @@ import Container from './container.vue'
import electricityGauge from './electricityGauge.vue' import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue' import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart }, components: { Container, electricityGauge, verticalBarChart },
@@ -87,12 +81,7 @@ export default {
return 0; return 0;
} }
return value; return value;
}, }
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如89代表89%
* @returns {0|1} flag值
*/
} }
} }
</script> </script>

View File

@@ -26,39 +26,26 @@
grid-template-columns: 1624px; grid-template-columns: 1624px;
"> ">
<operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" /> <operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" />
<!-- <keyWork /> -->
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
import ReportHeader from "../components/noRouterHeader.vue"; import ReportHeader from "../components/noRouterHeader.vue";
import { Sidebar } from "../../../layout/components"; import { Sidebar } from "../../../layout/components";
import screenfull from "screenfull"; import screenfull from "screenfull";
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
// import premProdStatus from "./components/premProdStatus.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import operatingLineChart from "../expenseAnalysisComponents/operatingLineChart"; import operatingLineChart from "../expenseAnalysisComponents/operatingLineChart";
import operatingLineChartCumulative from "../expenseAnalysisComponents/operatingLineChartCumulative.vue"; import operatingLineChartCumulative from "../expenseAnalysisComponents/operatingLineChartCumulative.vue";
import { getExpenseAnalysisGroupData } from '@/api/cockpit' import { getExpenseAnalysisGroupData } from '@/api/cockpit'
import moment from "moment";
export default { export default {
name: "DayReport", name: "DayReport",
components: { components: {
ReportHeader, ReportHeader,
operatingLineChartCumulative, operatingLineChartCumulative,
operatingLineChart, operatingLineChart,
// premProdStatus,
Sidebar, Sidebar,
}, },
data() { data() {
@@ -152,12 +139,6 @@ export default {
console.log(res); console.log(res);
this.monthData = res.data.month this.monthData = res.data.month
this.ytdData = res.data.ytd this.ytdData = res.data.ytd
// this.saleData = res.data.SaleData
// this.premiumProduct = res.data.premiumProduct
// this.salesTrendMap = res.data.salesTrendMap
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
}) })
}, },
handleTimeChange(obj) { handleTimeChange(obj) {
@@ -207,28 +188,7 @@ export default {
return false; return false;
} }
screenfull.toggle(this.$refs.dayReportB); screenfull.toggle(this.$refs.dayReportB);
}, }
// 导出
// 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> </script>

View File

@@ -52,14 +52,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
@@ -69,7 +61,6 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue"; import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../expenseAnalysisComponents/monthlyOverview.vue"; import monthlyOverview from "../expenseAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../expenseAnalysisComponents/totalOverview.vue"; import totalOverview from "../expenseAnalysisComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
import monthlyRelatedMetrics from "../expenseAnalysisComponents/monthlyRelatedMetrics.vue"; import monthlyRelatedMetrics from "../expenseAnalysisComponents/monthlyRelatedMetrics.vue";
import yearRelatedMetrics from "../expenseAnalysisComponents/yearRelatedMetrics.vue"; import yearRelatedMetrics from "../expenseAnalysisComponents/yearRelatedMetrics.vue";
import dataTrend from "../expenseAnalysisComponents/dataTrend.vue"; import dataTrend from "../expenseAnalysisComponents/dataTrend.vue";
@@ -77,15 +68,7 @@ import dataTrend from "../expenseAnalysisComponents/dataTrend.vue";
import profitLineChart from "../costComponents/profitLineChart.vue"; import profitLineChart from "../costComponents/profitLineChart.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import { getExpenseAnalysisFactoryData } from '@/api/cockpit' import { getExpenseAnalysisFactoryData } from '@/api/cockpit'
// import PSDO from "./components/PSDO.vue";
// import psiLineChart from "./components/psiLineChart.vue";
// import coreBottomLeft from "./components/coreBottomLeft.vue";
// import orderProgress from "./components/orderProgress.vue";
// import keyWork from "./components/keyWork.vue";
import moment from "moment"; import moment from "moment";
// import html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default { export default {
name: "DayReport", name: "DayReport",
components: { components: {
@@ -289,28 +272,7 @@ export default {
} else { } else {
clearInterval(this.timer); clearInterval(this.timer);
} }
}, }
// 导出
// () {
// 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> </script>

View File

@@ -26,20 +26,7 @@ export default {
trend: { trend: {
type: Array, type: Array,
// 默认值与实际数据结构一致12个月 // 默认值与实际数据结构一致12个月
default: () => [ default: () => []
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
]
}, },
}, },
data() { data() {
@@ -98,7 +85,7 @@ export default {
const diff = Number(item.diff) || 0; const diff = Number(item.diff) || 0;
// 计算达标标识≥100 → 1<100 → 0 // 计算达标标识≥100 → 1<100 → 0
const flag = this.getRateFlag(rate, real, budget); const flag = item.rate >= 100 ? 1 : 0;
// 填充数组 // 填充数组
months.push(month); months.push(month);
@@ -121,24 +108,6 @@ export default {
console.log('处理后的趋势数据:', this.chartData); console.log('处理后的趋势数据:', this.chartData);
}, },
/**
* 计算达标标识
* @param {Number} rate - 完成率原始值如1.2 → 120%
* @returns {Number} 1: 达标≥100%0: 未达标(<100%
*/
getRateFlag(rate, real, target) {
// 先处理无效值的情况
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 实际值和目标值都为0时算作达标
if (real === 0 && target === 0 && rate === 0) {
return 1; // 达标
}
// 其他情况rate >= 100 或 rate === 0 时达标
return (rate >= 100) ? 1 : 0;
}
}, },
}; };
</script> </script>

View File

@@ -145,86 +145,7 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 14, barWidth: 14,
label: { label: { show: false },
show: true,
position: 'top',
offset: [0, 0],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: function (params) {
const diff = data.diffs || [];
const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0;
const currentFlag = flags[params.dataIndex] || 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)' }, // 顶部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: {
text: {
// 缩小宽度和内边距适配68px容器
width: 'auto', // 自动宽度替代固定40px
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: { itemStyle: {
color: (params) => { color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色) // 达标状态1=达标绿色0=未达标(橙色)
@@ -252,6 +173,82 @@ export default {
borderWidth: 0 borderWidth: 0
}, },
data: data.reals // 实际销量(万元) data: data.reals // 实际销量(万元)
},
// 实际柱状图的标签层独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: { show: false },
data: (data.reals || []).map((value, index) => ({
value: value,
label: {
show: true,
position: 'top',
offset: [0, 0],
width: 68,
height: 20,
formatter: () => {
const diff = data.diffs || [];
const flags = data.flags || [];
const currentDiff = diff[index] || 0;
const currentFlag = flags[index] || 0;
if (currentFlag === 1) {
return `{achieved|${currentDiff}}{text|差值}`;
} else {
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',
fontSize: 11,
lineHeight: 20
},
unachieved: {
width: 'auto',
padding: [5, 0, 5, 10],
align: 'center',
color: '#F9A44A',
fontSize: 11,
lineHeight: 20
}
}
}
}))
} }
] ]
}; };

View File

@@ -19,9 +19,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt=""> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<verticalBarChart :detailData="monthData"> <verticalBarChart :detailData="monthData">
@@ -38,9 +35,6 @@ import Container from './container.vue'
import electricityGauge from './electricityGauge.vue' import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue' import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart }, components: { Container, electricityGauge, verticalBarChart },
@@ -65,29 +59,6 @@ export default {
} }
}, },
watch: {
// itemData: {
// handler(newValue, oldValue) {
// // this.updateChart()
// },
// 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() { mounted() {
// 初始化图表(若需展示图表,需在模板中添加对应 DOM // 初始化图表(若需展示图表,需在模板中添加对应 DOM
// this.$nextTick(() => this.updateChart()) // this.$nextTick(() => this.updateChart())

View File

@@ -69,7 +69,7 @@ export default {
const data = list.find(item => item && item.title === def.name) || fallback const data = list.find(item => item && item.title === def.name) || fallback
const detailData = { const detailData = {
...data, ...data,
flag: _this.getRateFlag(data.rate, data.real, data.budget), flag: data.rate>=100?1:0
} }
return { return {
...def, ...def,
@@ -100,18 +100,6 @@ export default {
deep: true, deep: true,
immediate: true // 初始化立即执行 immediate: true // 初始化立即执行
} }
},
mounted() {},
methods: {
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;
}
} }
} }
</script> </script>

View File

@@ -173,26 +173,58 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 40, barWidth: 40,
label: { show: false },
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
},
// 实际柱状图的标签层独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: { show: false },
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [32, 0], offset: [32, 0],
width: 100, width: 100,
height: 22, height: 22,
formatter: (params) => { formatter: () => {
const diff = data.diff || []; const diff = data.diff || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
// const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -225,45 +257,19 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 14 fontSize: 14
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 14 fontSize: 14
} }
} }
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
} }
] ]
}; };

View File

@@ -86,23 +86,6 @@ export default {
sortChange(value) { sortChange(value) {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断rate对应的flag值<1为0>1为1
* @param {number} rate 处理后的rate值已*100
* @returns {0|1} flag值
*/
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 (real <= target) return 1;
// 其他情况 => 未达标
return 0;
},
/** /**
* 核心处理函数:在所有数据都准备好后,才组装 chartData * 核心处理函数:在所有数据都准备好后,才组装 chartData
*/ */
@@ -116,7 +99,7 @@ export default {
const groupReal = [this.groupData.real]; // 实际值数组 const groupReal = [this.groupData.real]; // 实际值数组
const groupRate = [this.groupData.rate]; // 完成率数组 const groupRate = [this.groupData.rate]; // 完成率数组
// 新增集团rate对应的flag // 新增集团rate对应的flag
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; const groupFlag = [this.groupData.rate >= 100 ? 1 : 0];
console.log('集团数据数组:', { console.log('集团数据数组:', {
groupTarget, groupTarget,
@@ -137,7 +120,7 @@ export default {
const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryRate = this.factoryData.map(item => item.rate || 0);
const factoryDiff = this.factoryData.map(item => item.diff || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0);
// 新增每个工厂rate对应的flag数组 // 新增每个工厂rate对应的flag数组
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
// 3. 组装最终的chartData供子组件使用 // 3. 组装最终的chartData供子组件使用
this.chartData = { this.chartData = {

View File

@@ -81,23 +81,6 @@ export default {
sortChange(value) { sortChange(value) {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断rate对应的flag值<1为0>1为1
* @param {number} rate 处理后的rate值已*100
* @returns {0|1} flag值
*/
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 (real <= target) return 1;
// 其他情况 => 未达标
return 0;
},
/** /**
* 核心处理函数:在所有数据都准备好后,才组装 chartData * 核心处理函数:在所有数据都准备好后,才组装 chartData
*/ */
@@ -111,7 +94,7 @@ export default {
const groupReal = [this.groupData.real]; // 实际值数组 const groupReal = [this.groupData.real]; // 实际值数组
const groupRate = [this.groupData.rate]; // 完成率数组 const groupRate = [this.groupData.rate]; // 完成率数组
// 新增集团rate对应的flag // 新增集团rate对应的flag
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; const groupFlag = [this.groupData.rate > 100 ? 1 : 0];
console.log('集团数据数组:', { console.log('集团数据数组:', {
groupTarget, groupTarget,
groupDiff, groupDiff,
@@ -131,7 +114,7 @@ export default {
const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryRate = this.factoryData.map(item => item.rate || 0);
const factoryDiff = this.factoryData.map(item => item.diff || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0);
// 新增每个工厂rate对应的flag数组 // 新增每个工厂rate对应的flag数组
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); const factoryFlags = this.factoryData.map(item => item.rate > 100 ? 1 : 0);
console.log('factoryFlags', factoryFlags); console.log('factoryFlags', factoryFlags);
// 3. 组装最终的chartData供子组件使用 // 3. 组装最终的chartData供子组件使用

View File

@@ -19,9 +19,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt=""> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge :id="'totalG'" :detailData="ytdData" id="totalGauge"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData"> <verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
@@ -38,13 +35,9 @@ import Container from './container.vue'
import electricityGauge from './electricityGauge.vue' import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue' import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart }, components: { Container, electricityGauge, verticalBarChart },
// mixins: [resize],
props: { props: {
ytdData: { // 接收父组件传递的设备数据数组 ytdData: { // 接收父组件传递的设备数据数组
type: Object, type: Object,
@@ -73,21 +66,6 @@ export default {
deep: true // 若对象内属性变化需触发,需加 deep: true 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() { mounted() {
// 初始化图表(若需展示图表,需在模板中添加对应 DOM // 初始化图表(若需展示图表,需在模板中添加对应 DOM
// this.$nextTick(() => this.updateChart()) // this.$nextTick(() => this.updateChart())

View File

@@ -50,18 +50,6 @@ export default {
} }
}, },
methods: { methods: {
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;
},
updateChart() { updateChart() {
const chartDom = this.$refs[this.refName]; const chartDom = this.$refs[this.refName];
if (!chartDom) { if (!chartDom) {
@@ -76,7 +64,7 @@ export default {
this.myChart = echarts.init(chartDom); this.myChart = echarts.init(chartDom);
const diff = this.detailData.diff || 0 const diff = this.detailData.diff || 0
const rate = this.detailData.rate || 0 const rate = this.detailData.rate || 0
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0 const flagValue = this.detailData.rate >= 100 ? 1 : 0
this.flag = flagValue this.flag = flagValue
const option = { const option = {
@@ -87,17 +75,7 @@ export default {
label: { label: {
backgroundColor: '#6a7985' backgroundColor: '#6a7985'
} }
}, }
// formatter: (params) => {
// let html = `${params[0].axisValue}<br/>`;
// params.forEach(item => {
// const unit = item.seriesName === '完成率' ? '%' : (
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
// );
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
// });
// return html;
// }
}, },
grid: { grid: {
top: 40, top: 40,
@@ -110,9 +88,6 @@ export default {
xAxis: { xAxis: {
// 横向柱状图的x轴必须设为数值轴否则无法正常展示数值 // 横向柱状图的x轴必须设为数值轴否则无法正常展示数值
type: 'value', type: 'value',
// offset: 0,
// boundaryGap: true ,
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
axisTick: { show: false }, axisTick: { show: false },
min: 0, min: 0,
// //

View File

@@ -74,7 +74,7 @@ export default {
const data = list.find(item => item && item.title === def.name) || fallback const data = list.find(item => item && item.title === def.name) || fallback
const detailData = { const detailData = {
...data, ...data,
flag: _this.getRateFlag(data.rate, data.real, data.budget), flag: data.rate>=100?1:0
} }
return { return {
...def, ...def,
@@ -105,18 +105,6 @@ export default {
deep: true, deep: true,
immediate: true // 初始化立即执行 immediate: true // 初始化立即执行
} }
},
mounted() {},
methods: {
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;
}
} }
} }
</script> </script>

View File

@@ -25,26 +25,15 @@
grid-template-columns: 1624px; grid-template-columns: 1624px;
"> ">
<operatingLineChartCumulative :dateData="dateData" :totalData="totalData" /> <operatingLineChartCumulative :dateData="dateData" :totalData="totalData" />
<!-- <keyWork /> -->
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
import ReportHeader from "../components/noRouterHeader.vue"; import ReportHeader from "../components/noRouterHeader.vue";
import { Sidebar } from "../../../layout/components"; import { Sidebar } from "../../../layout/components";
import screenfull from "screenfull"; import screenfull from "screenfull";
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
// import premProdStatus from "./components/premProdStatus.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import operatingLineChart from "../fullCostAnalysisComponents/operatingLineChart"; import operatingLineChart from "../fullCostAnalysisComponents/operatingLineChart";
import operatingLineChartCumulative from "../fullCostAnalysisComponents/operatingLineChartCumulative.vue"; import operatingLineChartCumulative from "../fullCostAnalysisComponents/operatingLineChartCumulative.vue";
@@ -57,7 +46,6 @@ export default {
ReportHeader, ReportHeader,
operatingLineChartCumulative, operatingLineChartCumulative,
operatingLineChart, operatingLineChart,
// premProdStatus,
Sidebar, Sidebar,
}, },
data() { data() {
@@ -97,13 +85,11 @@ export default {
variables() { variables() {
return variables; return variables;
}, },
// ...mapGetters(['sidebar']),
styles() { styles() {
const v = Math.floor(this.value * this.beilv * 100) / 10000; const v = Math.floor(this.value * this.beilv * 100) / 10000;
return { return {
transform: `scale(${v})`, transform: `scale(${v})`,
transformOrigin: "left top", transformOrigin: "left top",
// overflow: hidden;
}; };
}, },
}, },
@@ -148,12 +134,6 @@ export default {
console.log(res); console.log(res);
this.thisMonData = res.data.thisMonData this.thisMonData = res.data.thisMonData
this.totalData = res.data.totalData this.totalData = res.data.totalData
// this.saleData = res.data.SaleData
// this.premiumProduct = res.data.premiumProduct
// this.salesTrendMap = res.data.salesTrendMap
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
}) })
}, },
handleTimeChange(obj) { handleTimeChange(obj) {
@@ -203,28 +183,7 @@ export default {
return false; return false;
} }
screenfull.toggle(this.$refs.dayReportB); screenfull.toggle(this.$refs.dayReportB);
}, }
// 导出
// 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> </script>

View File

@@ -52,14 +52,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
@@ -69,20 +61,11 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue"; import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../fullCostAnalysisComponents/monthlyOverview.vue"; import monthlyOverview from "../fullCostAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../fullCostAnalysisComponents/totalOverview.vue"; import totalOverview from "../fullCostAnalysisComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
import relatedIndicatorsAnalysis from "../fullCostAnalysisComponents/relatedIndicatorsAnalysis.vue"; import relatedIndicatorsAnalysis from "../fullCostAnalysisComponents/relatedIndicatorsAnalysis.vue";
import dataTrend from "../fullCostAnalysisComponents/dataTrend.vue"; import dataTrend from "../fullCostAnalysisComponents/dataTrend.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import { getUnitPriceAnalysisBaseData } from '@/api/cockpit' import { getUnitPriceAnalysisBaseData } from '@/api/cockpit'
// import PSDO from "./components/PSDO.vue";
// import psiLineChart from "./components/psiLineChart.vue";
// import coreBottomLeft from "./components/coreBottomLeft.vue";
// import orderProgress from "./components/orderProgress.vue";
// import keyWork from "./components/keyWork.vue";
import moment from "moment"; import moment from "moment";
// import html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default { export default {
name: "DayReport", name: "DayReport",
components: { components: {
@@ -93,7 +76,6 @@ export default {
totalOverview, totalOverview,
dataTrend, dataTrend,
relatedIndicatorsAnalysis relatedIndicatorsAnalysis
// psiLineChart
}, },
data() { data() {
return { return {
@@ -108,7 +90,6 @@ export default {
totalData: {}, totalData: {},
trend: {}, trend: {},
relatedData: {}, relatedData: {},
// cusProData: {},
}; };
}, },
@@ -192,34 +173,19 @@ export default {
const requestParams = { const requestParams = {
startTime: this.dateData.startTime, startTime: this.dateData.startTime,
endTime: this.dateData.endTime, endTime: this.dateData.endTime,
// index: this.index,
// sort: 1,
paramName: '全成本', paramName: '全成本',
paramList: ['制造成本', '财务费用', '销售费用', '管理费用', '运费'], paramList: ['制造成本', '财务费用', '销售费用', '管理费用', '运费'],
baseId: this.factory, baseId: this.factory,
// baseId: Number(this.factory),
}; };
// 调用接口 // 调用接口
getUnitPriceAnalysisBaseData(requestParams).then((res) => { getUnitPriceAnalysisBaseData(requestParams).then((res) => {
this.monData = res.data.monData this.monData = res.data.monData
this.totalData = res.data.totalData this.totalData = res.data.totalData
// this.relatedMon = res.data.relatedMon
this.relatedData = { this.relatedData = {
relatedTotal: res.data.relatedTotal, relatedTotal: res.data.relatedTotal,
relatedMon: res.data.relatedMon, relatedMon: res.data.relatedMon,
} }
this.trend = res.data.trend this.trend = res.data.trend
// this.relatedTotal = res.data.relatedTotal
// this.cusProData = {
// customerPriceMon: res.data.customerPriceMon,
// customerPriceTotal: res.data.customerPriceTotal,
// customerSaleMon: res.data.customerSaleMon,
// customerSaleTotal: res.data.customerSaleTotal,
// productMonSale: res.data.productMonSale,
// productPriceMon: res.data.productPriceMon,
// productPriceTotal: res.data.productPriceTotal,
// productTotalSale: res.data.productTotalSale,
// }
}); });
}, },
@@ -229,7 +195,6 @@ export default {
this.dateData = { this.dateData = {
startTime: obj.startTime, startTime: obj.startTime,
endTime: obj.endTime, endTime: obj.endTime,
// mode: obj.mode,
} }
this.getData() this.getData()
@@ -286,35 +251,12 @@ export default {
}, },
changeDate(val) { changeDate(val) {
this.date = val; this.date = val;
// this.weekDay = this.weekArr[moment(this.date).format('e')]
// this.getData()
if (this.date === moment().format("yyyy-MM-DD")) { if (this.date === moment().format("yyyy-MM-DD")) {
this.loopTime(); this.loopTime();
} else { } else {
clearInterval(this.timer); clearInterval(this.timer);
} }
}, }
// 导出
// () {
// 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> </script>

View File

@@ -110,9 +110,7 @@ export default {
reals.push(monthData.real || 0); reals.push(monthData.real || 0);
targets.push(monthData.target || 0); targets.push(monthData.target || 0);
diffs.push(monthData.diff || 0); diffs.push(monthData.diff || 0);
flags.push(completeRate>=100?1:0);
// 生成达标状态(复用 getRateFlag 逻辑)
flags.push(this.getRateFlag(completeRate, monthData.real, monthData.target));
}); });
return { return {
@@ -191,84 +189,7 @@ export default {
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 14, barWidth: 14,
label: { label: {
show: true, show: false
position: 'top',
offset: [0, 0],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: function (params) {
const diff = diffs || [];
// const flags = flags || [];
const currentDiff = diff[params.dataIndex] || 0;
const currentFlag = flags[params.dataIndex] || 0;
console.log('currentFlag', flags, params.dataIndex);
// 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)' }, // 顶部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: {
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: { itemStyle: {
color: (params) => { color: (params) => {
@@ -295,6 +216,82 @@ export default {
borderWidth: 0 borderWidth: 0
}, },
data: reals, // 动态实际值 data: reals, // 动态实际值
},
// 4. 实际差值标签独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: {
show: false
},
data: (reals || []).map((value, index) => ({
value: value,
label: {
show: true,
position: 'top',
offset: [0, 0],
width: 68,
height: 20,
formatter: () => {
const currentDiff = diffs[index] || 0;
const currentFlag = flags[index] || 0;
if (currentFlag === 1) {
return `{achieved|${currentDiff}}{text|差值}`;
} else {
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',
fontSize: 11,
lineHeight: 20
},
unachieved: {
width: 'auto',
padding: [5, 0, 5, 10],
align: 'center',
color: '#F9A44A',
fontSize: 11,
lineHeight: 20
}
}
}
}))
} }
] ]
}; };
@@ -309,19 +306,6 @@ export default {
this.unit = item.unit; this.unit = item.unit;
this.isDropdownShow = false; this.isDropdownShow = false;
}, },
// 复用达标状态判断方法
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;
},
} }
}; };
</script> </script>

View File

@@ -17,10 +17,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降"> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge"> -->
<!-- 传递包含flag的factoryData给仪表盘组件 -->
<!-- <electricityGauge id="month" :detailData="factoryData"></electricityGauge> -->
<!-- </div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<!-- 传递包含flag的factoryData给柱状图组件 --> <!-- 传递包含flag的factoryData给柱状图组件 -->
@@ -87,7 +83,7 @@ export default {
target: 0, target: 0,
thb: 0, thb: 0,
...rawData, ...rawData,
flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段 flag: rawData.completeRate >= 100 ? 1 : 0
}; };
} }
}, },
@@ -101,23 +97,6 @@ export default {
} }
return value; return value;
}, },
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如89代表89%
* @returns {0|1} flag值
*/
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;
},
} }
} }
</script> </script>

View File

@@ -174,26 +174,62 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 40, barWidth: 40,
label: {
show: false
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
},
// 实际差值标签独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: {
show: false
},
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [32, 0], offset: [32, 0],
width: 100, width: 100,
height: 22, height: 22,
formatter: (params) => { formatter: () => {
const diff = data.diff || []; const diff = data.diff || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
// const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -226,45 +262,19 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 14 fontSize: 14
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 14 fontSize: 14
} }
} }
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
} }
] ]
}; };

View File

@@ -80,24 +80,6 @@ export default {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如80代表80%
* @returns {0|1} flag值
*/
getRateFlag(rate, real, target) {
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 1. 完成率 >= 100 => 达标
if (rate >= 100) return 1;
// 2. 实际小于等于目标) => 达标
if (real <= target) return 1;
// 其他情况 => 未达标
return 0;
},
/** /**
* 核心处理函数解析thisMonData组装集团和工厂数据 * 核心处理函数解析thisMonData组装集团和工厂数据
*/ */
@@ -116,7 +98,7 @@ getRateFlag(rate, real, target) {
diff: [ksxnData.diff], diff: [ksxnData.diff],
reals: [ksxnData.real], reals: [ksxnData.real],
rate: [ksxnData.completeRate], rate: [ksxnData.completeRate],
flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)], flags: [ksxnData.completeRate >= 100 ? 1 : 0],
thb: [ksxnData.thb] // 新增thb字段如果子组件需要 thb: [ksxnData.thb] // 新增thb字段如果子组件需要
}; };
@@ -131,7 +113,7 @@ getRateFlag(rate, real, target) {
diff: factoryDataList.map(item => item.diff || 0), // 差值 diff: factoryDataList.map(item => item.diff || 0), // 差值
reals: factoryDataList.map(item => item.real || 0), // 实际值 reals: factoryDataList.map(item => item.real || 0), // 实际值
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率 rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识 flags: factoryDataList.map(item => item.completeRate >= 100 ? 1 : 0), // 完成率标识
thb: factoryDataList.map(item => item.thb || 0) // thb字段 thb: factoryDataList.map(item => item.thb || 0) // thb字段
}; };

View File

@@ -80,23 +80,6 @@ export default {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如80代表80%
* @returns {0|1} flag值
*/
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;
},
/** /**
* 核心处理函数解析thisMonData组装集团和工厂数据 * 核心处理函数解析thisMonData组装集团和工厂数据
@@ -116,7 +99,7 @@ getRateFlag(rate, real, target) {
diff: [ksxnData.diff], diff: [ksxnData.diff],
reals: [ksxnData.real], reals: [ksxnData.real],
rate: [ksxnData.completeRate], rate: [ksxnData.completeRate],
flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)], flags: [ksxnData.completeRate>=100?1:0],
thb: [ksxnData.thb] // 新增thb字段如果子组件需要 thb: [ksxnData.thb] // 新增thb字段如果子组件需要
}; };
@@ -131,7 +114,7 @@ getRateFlag(rate, real, target) {
diff: factoryDataList.map(item => item.diff || 0), // 差值 diff: factoryDataList.map(item => item.diff || 0), // 差值
reals: factoryDataList.map(item => item.real || 0), // 实际值 reals: factoryDataList.map(item => item.real || 0), // 实际值
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率 rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识 flags: factoryDataList.map(item => item.completeRate>=100?1:0), // 完成率标识
thb: factoryDataList.map(item => item.thb || 0) // thb字段 thb: factoryDataList.map(item => item.thb || 0) // thb字段
}; };

View File

@@ -28,9 +28,6 @@
import Container from '../components/container.vue' import Container from '../components/container.vue'
import operatingSingleBar from './operatingSingleBar.vue' import operatingSingleBar from './operatingSingleBar.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, operatingSingleBar }, components: { Container, operatingSingleBar },
@@ -100,7 +97,7 @@ export default {
const data = list.find(item => item && item.title === def.name) || fallback const data = list.find(item => item && item.title === def.name) || fallback
const detailData = { const detailData = {
...data, ...data,
flag: _this.getRateFlag((data || _this.defaultData).completeRate, (data || _this.defaultData).real, (data || _this.defaultData).target), flag:data.completeRate > 100 ? 1 : 0,
} }
return { return {
...def, ...def,
@@ -158,18 +155,6 @@ export default {
} }
}) })
}, },
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;
},
handleChange(value) { handleChange(value) {
console.log('value', value, this.relatedData); console.log('value', value, this.relatedData);
this.currentTab = value; this.currentTab = value;

View File

@@ -19,9 +19,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt=""> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge id="year" :detailData="factoryData"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<verticalBarChart :detailData="factoryData"> <verticalBarChart :detailData="factoryData">
@@ -38,9 +35,6 @@ import Container from './container.vue'
import electricityGauge from './electricityGauge.vue' import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue' import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart }, components: { Container, electricityGauge, verticalBarChart },
@@ -90,7 +84,7 @@ export default {
target: 0, target: 0,
thb: 0, thb: 0,
...rawData, ...rawData,
flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段 flag: rawData.completeRate >= 100 ? 1 : 0 // 根据完成率计算flag
}; };
} }
}, },
@@ -104,23 +98,6 @@ export default {
} }
return value; return value;
}, },
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如89代表89%
* @returns {0|1} flag值
*/
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;
},
} }
} }
</script> </script>

View File

@@ -26,39 +26,26 @@
grid-template-columns: 1624px; grid-template-columns: 1624px;
"> ">
<operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" /> <operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" />
<!-- <keyWork /> -->
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
import ReportHeader from "../components/noRouterHeader.vue"; import ReportHeader from "../components/noRouterHeader.vue";
import { Sidebar } from "../../../layout/components"; import { Sidebar } from "../../../layout/components";
import screenfull from "screenfull"; import screenfull from "screenfull";
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
// import premProdStatus from "./components/premProdStatus.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import operatingLineChart from "../grossMarginComponents/operatingLineChart"; import operatingLineChart from "../grossMarginComponents/operatingLineChart";
import operatingLineChartCumulative from "../grossMarginComponents/operatingLineChartCumulative.vue"; import operatingLineChartCumulative from "../grossMarginComponents/operatingLineChartCumulative.vue";
import { getGrossMarginGroupData } from '@/api/cockpit' import { getGrossMarginGroupData } from '@/api/cockpit'
import moment from "moment";
export default { export default {
name: "DayReport", name: "DayReport",
components: { components: {
ReportHeader, ReportHeader,
operatingLineChartCumulative, operatingLineChartCumulative,
operatingLineChart, operatingLineChart,
// premProdStatus,
Sidebar, Sidebar,
}, },
data() { data() {
@@ -157,12 +144,6 @@ export default {
console.log(res); console.log(res);
this.monthData = res.data.month this.monthData = res.data.month
this.ytdData = res.data.ytd this.ytdData = res.data.ytd
// this.saleData = res.data.SaleData
// this.premiumProduct = res.data.premiumProduct
// this.salesTrendMap = res.data.salesTrendMap
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
}) })
}, },
handleTimeChange(obj) { handleTimeChange(obj) {
@@ -212,28 +193,7 @@ export default {
return false; return false;
} }
screenfull.toggle(this.$refs.dayReportB); screenfull.toggle(this.$refs.dayReportB);
}, }
// 导出
// 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> </script>

View File

@@ -53,14 +53,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
@@ -70,7 +62,6 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue"; import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../grossMarginComponents/monthlyOverview.vue"; import monthlyOverview from "../grossMarginComponents/monthlyOverview.vue";
import totalOverview from "../grossMarginComponents/totalOverview.vue"; import totalOverview from "../grossMarginComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
import monthlyRelatedMetrics from "../grossMarginComponents/monthlyRelatedMetrics.vue"; import monthlyRelatedMetrics from "../grossMarginComponents/monthlyRelatedMetrics.vue";
import yearRelatedMetrics from "../grossMarginComponents/yearRelatedMetrics.vue"; import yearRelatedMetrics from "../grossMarginComponents/yearRelatedMetrics.vue";
import dataTrend from "../grossMarginComponents/dataTrend.vue"; import dataTrend from "../grossMarginComponents/dataTrend.vue";
@@ -78,15 +69,7 @@ import dataTrend from "../grossMarginComponents/dataTrend.vue";
import profitLineChart from "../costComponents/profitLineChart.vue"; import profitLineChart from "../costComponents/profitLineChart.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import { getGrossMarginFactoryData } from '@/api/cockpit' import { getGrossMarginFactoryData } from '@/api/cockpit'
// import PSDO from "./components/PSDO.vue";
// import psiLineChart from "./components/psiLineChart.vue";
// import coreBottomLeft from "./components/coreBottomLeft.vue";
// import orderProgress from "./components/orderProgress.vue";
// import keyWork from "./components/keyWork.vue";
import moment from "moment"; import moment from "moment";
// import html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default { export default {
name: "DayReport", name: "DayReport",
components: { components: {
@@ -284,35 +267,12 @@ export default {
}, },
changeDate(val) { changeDate(val) {
this.date = val; this.date = val;
// this.weekDay = this.weekArr[moment(this.date).format('e')]
// this.getData()
if (this.date === moment().format("yyyy-MM-DD")) { if (this.date === moment().format("yyyy-MM-DD")) {
this.loopTime(); this.loopTime();
} else { } else {
clearInterval(this.timer); clearInterval(this.timer);
} }
}, },
// 导出
// () {
// 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> </script>

View File

@@ -27,18 +27,6 @@ export default {
type: Array, type: Array,
// 默认值与实际数据结构一致12个月 // 默认值与实际数据结构一致12个月
default: () => [ default: () => [
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
] ]
}, },
}, },
@@ -98,7 +86,7 @@ export default {
const diff = Number(item.diff) || 0; const diff = Number(item.diff) || 0;
// 计算达标标识≥100 → 1<100 → 0 // 计算达标标识≥100 → 1<100 → 0
const flag = this.getRateFlag(rate, real, budget); const flag = item.rate >= 100 ? 1 : 0;
// 填充数组 // 填充数组
months.push(month); months.push(month);
@@ -120,25 +108,7 @@ export default {
}; };
console.log('处理后的趋势数据:', this.chartData); console.log('处理后的趋势数据:', this.chartData);
}, }
/**
* 计算达标标识
* @param {Number} rate - 完成率原始值如1.2 → 120%
* @returns {Number} 1: 达标≥100%0: 未达标(<100%
*/
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;
},
}, },
}; };
</script> </script>

View File

@@ -146,28 +146,59 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 14, barWidth: 14,
label: { show: false },
itemStyle: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)
const safeFlag = data.flags;
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals // 实际销量(万元)
},
// 实际柱状图的标签层独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: { show: false },
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [0, 0], offset: [0, 0],
// 固定label尺寸68px×20px
width: 68, width: 68,
height: 20, height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸 formatter: () => {
formatter: (params) => {
const diff = data.diffs || []; const diff = data.diffs || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -201,48 +232,21 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 11, fontSize: 11,
lineHeight: 20 lineHeight: 20
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 11, fontSize: 11,
lineHeight: 20 lineHeight: 20
} }
} }
},
itemStyle: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)
const safeFlag = data.flags;
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals // 实际销量(万元)
} }
] ]
}; };

View File

@@ -19,9 +19,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt=""> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<verticalBarChart :detailData="monthData"> <verticalBarChart :detailData="monthData">
@@ -38,9 +35,6 @@ import Container from './container.vue'
import electricityGauge from './electricityGauge.vue' import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue' import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart }, components: { Container, electricityGauge, verticalBarChart },
@@ -74,21 +68,6 @@ export default {
deep: true // 若对象内属性变化需触发,需加 deep: true 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() { mounted() {
// 初始化图表(若需展示图表,需在模板中添加对应 DOM // 初始化图表(若需展示图表,需在模板中添加对应 DOM
// this.$nextTick(() => this.updateChart()) // this.$nextTick(() => this.updateChart())

View File

@@ -96,18 +96,6 @@ export default {
} }
}) })
}, },
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;
},
updateChart(data) { updateChart(data) {
// 修复核心:正确获取默认值(调用 factory 函数) // 修复核心:正确获取默认值(调用 factory 函数)
@@ -124,12 +112,12 @@ getRateFlag(rate, real, target) {
// 整合flag字段 // 整合flag字段
this.ytdIncomeData = { this.ytdIncomeData = {
...incomeItem, ...incomeItem,
flag: this.getRateFlag(incomeItem.rate, incomeItem.real, incomeItem.budget) flag: incomeItem.rate > 100 ? 1 : 0
}; };
this.ytdCostData = { this.ytdCostData = {
...costItem, ...costItem,
flag: this.getRateFlag(costItem.rate, costItem.real, costItem.budget) flag: costItem.rate > 100 ? 1 : 0
}; };
console.log('累计收入数据:', this.ytdIncomeData); console.log('累计收入数据:', this.ytdIncomeData);

View File

@@ -174,26 +174,58 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 40, barWidth: 40,
label: { show: false },
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
},
// 实际柱状图的标签层独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: { show: false },
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [32, 0], offset: [32, 0],
width: 100, width: 100,
height: 22, height: 22,
formatter: (params) => { formatter: () => {
const diff = data.diff || []; const diff = data.diff || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -226,45 +258,19 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 14 fontSize: 14
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 14 fontSize: 14
} }
} }
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
} }
] ]
}; };

View File

@@ -17,10 +17,6 @@ export default {
type: Object, type: Object,
default: () => ({ default: () => ({
}), }),
// 校验数据格式
// validator: (value) => {
// return Array.isArray(value.series) && Array.isArray(value.allPlaceNames);
// }
} }
}, },
mounted() { mounted() {
@@ -72,16 +68,6 @@ export default {
backgroundColor: '#6a7985' backgroundColor: '#6a7985'
} }
}, },
// formatter: (params) => {
// let html = `${params[0].axisValue}<br/>`;
// params.forEach(item => {
// const unit = item.seriesName === '完成率' ? '%' : (
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
// );
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
// });
// return html;
// }
}, },
grid: { grid: {
top: 30, top: 30,

View File

@@ -118,7 +118,7 @@ getRateFlag(rate, real, target) {
const groupReal = [this.groupData.real]; // 实际值数组 const groupReal = [this.groupData.real]; // 实际值数组
const groupRate = [this.groupData.rate]; // 完成率数组 const groupRate = [this.groupData.rate]; // 完成率数组
// 新增集团rate对应的flag // 新增集团rate对应的flag
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; const groupFlag = [this.groupData.rate >=100 ? 1 : 0];
console.log('集团数据数组:', { console.log('集团数据数组:', {
groupTarget, groupTarget,
@@ -139,7 +139,7 @@ getRateFlag(rate, real, target) {
const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryRate = this.factoryData.map(item => item.rate || 0);
const factoryDiff = this.factoryData.map(item => item.diff || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0);
// 新增每个工厂rate对应的flag数组 // 新增每个工厂rate对应的flag数组
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); const factoryFlags = this.factoryData.map(item => item.rate >=100 ? 1 : 0);
// 3. 组装最终的chartData供子组件使用 // 3. 组装最终的chartData供子组件使用
this.chartData = { this.chartData = {

View File

@@ -81,24 +81,6 @@ export default {
sortChange(value) { sortChange(value) {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断rate对应的flag值<1为0>1为1
* @param {number} rate 处理后的rate值已*100
* @returns {0|1} flag值
*/
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;
},
/** /**
* 核心处理函数:在所有数据都准备好后,才组装 chartData * 核心处理函数:在所有数据都准备好后,才组装 chartData
@@ -113,7 +95,7 @@ getRateFlag(rate, real, target) {
const groupReal = [this.groupData.real]; // 实际值数组 const groupReal = [this.groupData.real]; // 实际值数组
const groupRate = [this.groupData.rate]; // 完成率数组 const groupRate = [this.groupData.rate]; // 完成率数组
// 新增集团rate对应的flag // 新增集团rate对应的flag
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; const groupFlag = [this.groupData.rate > 100 ? 1 : 0];
console.log('集团数据数组:', { console.log('集团数据数组:', {
groupTarget, groupTarget,
@@ -134,7 +116,7 @@ getRateFlag(rate, real, target) {
const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryRate = this.factoryData.map(item => item.rate || 0);
const factoryDiff = this.factoryData.map(item => item.diff || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0);
// 新增每个工厂rate对应的flag数组 // 新增每个工厂rate对应的flag数组
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.rate, item.budget)); const factoryFlags = this.factoryData.map(item => item.rate > 100 ? 1 : 0);
// 3. 组装最终的chartData供子组件使用 // 3. 组装最终的chartData供子组件使用
this.chartData = { this.chartData = {

View File

@@ -40,33 +40,6 @@ export default {
const salesData = { const salesData = {
allPlaceNames: this.locations, allPlaceNames: this.locations,
series: [ series: [
// 1. 完成率(折线图)
// {
// 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(40, 138, 255, 0)' }
// ])
// },
// data: data.rates, // 完成率(%
// symbol: 'circle',
// symbolSize: 6
// },
// 2. 目标(柱状图)
{ {
name: '预算', name: '预算',
type: 'bar', type: 'bar',

View File

@@ -19,9 +19,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt=""> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge :id="'totalG'" :detailData="ytdData" id="totalGauge"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData"> <verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
@@ -38,9 +35,6 @@ import Container from './container.vue'
import electricityGauge from './electricityGauge.vue' import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue' import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart }, components: { Container, electricityGauge, verticalBarChart },
@@ -73,21 +67,6 @@ export default {
deep: true // 若对象内属性变化需触发,需加 deep: true 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() { mounted() {
// 初始化图表(若需展示图表,需在模板中添加对应 DOM // 初始化图表(若需展示图表,需在模板中添加对应 DOM
// this.$nextTick(() => this.updateChart()) // this.$nextTick(() => this.updateChart())

View File

@@ -50,18 +50,6 @@ export default {
} }
}, },
methods: { methods: {
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;
},
updateChart() { updateChart() {
const chartDom = this.$refs[this.refName]; const chartDom = this.$refs[this.refName];
if (!chartDom) { if (!chartDom) {
@@ -76,7 +64,7 @@ export default {
this.myChart = echarts.init(chartDom); this.myChart = echarts.init(chartDom);
const diff = this.detailData.diff || 0 const diff = this.detailData.diff || 0
const rate = this.detailData.rate || 0 const rate = this.detailData.rate || 0
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0 const flagValue = this.detailData.rate>=100?1:0
this.flag = flagValue this.flag = flagValue
const option = { const option = {
@@ -88,16 +76,6 @@ export default {
backgroundColor: '#6a7985' backgroundColor: '#6a7985'
} }
}, },
// formatter: (params) => {
// let html = `${params[0].axisValue}<br/>`;
// params.forEach(item => {
// const unit = item.seriesName === '完成率' ? '%' : (
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
// );
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
// });
// return html;
// }
}, },
grid: { grid: {
top: 40, top: 40,
@@ -110,9 +88,6 @@ export default {
xAxis: { xAxis: {
// 横向柱状图的x轴必须设为数值轴否则无法正常展示数值 // 横向柱状图的x轴必须设为数值轴否则无法正常展示数值
type: 'value', type: 'value',
// offset: 0,
// boundaryGap: true ,
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
axisTick: { show: false }, axisTick: { show: false },
min: 0, min: 0,
// //

View File

@@ -101,19 +101,6 @@ export default {
} }
}) })
}, },
// 保留原flag判断逻辑≥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;
},
updateChart(data) { updateChart(data) {
// 数据兜底确保是数组且长度≥2 // 数据兜底确保是数组且长度≥2
@@ -128,12 +115,12 @@ export default {
// 整合flag字段到收入/全成本数据中 // 整合flag字段到收入/全成本数据中
this.incomeData = { this.incomeData = {
...incomeItem, ...incomeItem,
flag: this.getRateFlag(incomeItem.rate, incomeItem.real, incomeItem.budget) flag: incomeItem.rate >= 100 ? 1 : 0
}; };
this.totalCostData = { this.totalCostData = {
...totalCostItem, ...totalCostItem,
flag: this.getRateFlag(totalCostItem.rate, totalCostItem.real, totalCostItem.budget) flag: totalCostItem.rate >= 100 ? 1 : 0
}; };
// 调试:确认数据赋值正确 // 调试:确认数据赋值正确

View File

@@ -26,39 +26,26 @@
grid-template-columns: 1624px; grid-template-columns: 1624px;
"> ">
<operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" /> <operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" />
<!-- <keyWork /> -->
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
import ReportHeader from "../components/noRouterHeader.vue"; import ReportHeader from "../components/noRouterHeader.vue";
import { Sidebar } from "../../../layout/components"; import { Sidebar } from "../../../layout/components";
import screenfull from "screenfull"; import screenfull from "screenfull";
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
// import premProdStatus from "./components/premProdStatus.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import operatingLineChart from "../inputOutputRatioComponents/operatingLineChart"; import operatingLineChart from "../inputOutputRatioComponents/operatingLineChart";
import operatingLineChartCumulative from "../inputOutputRatioComponents/operatingLineChartCumulative.vue"; import operatingLineChartCumulative from "../inputOutputRatioComponents/operatingLineChartCumulative.vue";
import { getInputOutputRateGroupData } from '@/api/cockpit' import { getInputOutputRateGroupData } from '@/api/cockpit'
import moment from "moment";
export default { export default {
name: "DayReport", name: "DayReport",
components: { components: {
ReportHeader, ReportHeader,
operatingLineChartCumulative, operatingLineChartCumulative,
operatingLineChart, operatingLineChart,
// premProdStatus,
Sidebar, Sidebar,
}, },
data() { data() {
@@ -201,28 +188,7 @@ export default {
return false; return false;
} }
screenfull.toggle(this.$refs.dayReportB); screenfull.toggle(this.$refs.dayReportB);
}, }
// 导出
// 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> </script>

View File

@@ -52,14 +52,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
@@ -69,21 +61,12 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue"; import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../inputOutputRatioComponents/monthlyOverview.vue"; import monthlyOverview from "../inputOutputRatioComponents/monthlyOverview.vue";
import totalOverview from "../inputOutputRatioComponents/totalOverview.vue"; import totalOverview from "../inputOutputRatioComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
import monthlyRelatedMetrics from "../inputOutputRatioComponents/monthlyRelatedMetrics.vue"; import monthlyRelatedMetrics from "../inputOutputRatioComponents/monthlyRelatedMetrics.vue";
import yearRelatedMetrics from "../inputOutputRatioComponents/yearRelatedMetrics.vue"; import yearRelatedMetrics from "../inputOutputRatioComponents/yearRelatedMetrics.vue";
import dataTrend from "../inputOutputRatioComponents/dataTrend.vue"; import dataTrend from "../inputOutputRatioComponents/dataTrend.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import { getInputOutputRateFactoryData } from '@/api/cockpit' import { getInputOutputRateFactoryData } from '@/api/cockpit'
// import PSDO from "./components/PSDO.vue";
// import psiLineChart from "./components/psiLineChart.vue";
// import coreBottomLeft from "./components/coreBottomLeft.vue";
// import orderProgress from "./components/orderProgress.vue";
// import keyWork from "./components/keyWork.vue";
import moment from "moment"; import moment from "moment";
// import html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default { export default {
name: "DayReport", name: "DayReport",
components: { components: {
@@ -112,11 +95,6 @@ export default {
monthAnalysis: [], monthAnalysis: [],
ytdAnalysis: [], ytdAnalysis: [],
trend: [], trend: [],
// trendData: [],
// parentItemList: [
// { name: "燃料成本", target: 0, value: 0, proportion: 0, flag: 1 },
// { name: "天然气", target: 0, value: 0, proportion: 0, flag: 1 }
// ],
}; };
}, },
@@ -287,28 +265,7 @@ export default {
} else { } else {
clearInterval(this.timer); clearInterval(this.timer);
} }
}, }
// 导出
// () {
// 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> </script>

View File

@@ -26,20 +26,7 @@ export default {
trend: { trend: {
type: Array, type: Array,
// 默认值与实际数据结构一致12个月 // 默认值与实际数据结构一致12个月
default: () => [ default: () => []
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
]
}, },
}, },
data() { data() {
@@ -96,9 +83,7 @@ export default {
const real = Number(item.real) || 0; const real = Number(item.real) || 0;
const rate = Number(item.rate) || 0; const rate = Number(item.rate) || 0;
const diff = Number(item.diff) || 0; const diff = Number(item.diff) || 0;
const flag = item.rate>=100 ? 1 : 0;
// 计算达标标识≥100 → 1<100 → 0
const flag = this.getRateFlag(rate,real,budget);
// 填充数组 // 填充数组
months.push(month); months.push(month);
@@ -120,25 +105,7 @@ export default {
}; };
console.log('处理后的趋势数据:', this.chartData); console.log('处理后的趋势数据:', this.chartData);
}, }
/**
* 计算达标标识
* @param {Number} rate - 完成率原始值如1.2 → 120%
* @returns {Number} 1: 达标≥100%0: 未达标(<100%
*/
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;
},
}, },
}; };
</script> </script>

View File

@@ -145,28 +145,59 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 14, barWidth: 14,
label: { show: false },
itemStyle: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)
const safeFlag = data.flags;
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals // 实际销量(万元)
},
// 实际柱状图的标签层独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: { show: false },
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [0, 0], offset: [0, 0],
// 固定label尺寸68px×20px
width: 68, width: 68,
height: 20, height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸 formatter: () => {
formatter: (params) => {
const diff = data.diffs || []; const diff = data.diffs || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -200,48 +231,21 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 11, fontSize: 11,
lineHeight: 20 lineHeight: 20
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 11, fontSize: 11,
lineHeight: 20 lineHeight: 20
} }
} }
},
itemStyle: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)
const safeFlag = data.flags;
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals // 实际销量(万元)
} }
] ]
}; };

View File

@@ -19,9 +19,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt=""> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<verticalBarChart :detailData="monthData"> <verticalBarChart :detailData="monthData">
@@ -38,9 +35,6 @@ import Container from './container.vue'
import electricityGauge from './electricityGauge.vue' import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue' import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart }, components: { Container, electricityGauge, verticalBarChart },
@@ -64,35 +58,6 @@ export default {
chart: null, chart: null,
} }
},
watch: {
// itemData: {
// handler(newValue, oldValue) {
// // this.updateChart()
// },
// 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())
},
methods: {
} }
} }
</script> </script>

View File

@@ -69,7 +69,7 @@ export default {
const data = list.find(item => item && item.title === def.name) || fallback const data = list.find(item => item && item.title === def.name) || fallback
const detailData = { const detailData = {
...data, ...data,
flag: _this.getRateFlag(data.rate, data.real, data.budget), flag: data.rate > 100 ? 1 : 0,
} }
return { return {
...def, ...def,
@@ -101,22 +101,6 @@ export default {
deep: true, deep: true,
immediate: true // 初始化立即执行 immediate: true // 初始化立即执行
} }
},
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;
}
} }
} }
</script> </script>

View File

@@ -174,26 +174,58 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 40, barWidth: 40,
label: { show: false },
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
},
// 实际柱状图的标签层独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: { show: false },
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [32, 0], offset: [32, 0],
width: 100, width: 100,
height: 22, height: 22,
formatter: (params) => { formatter: () => {
const diff = data.diff || []; const diff = data.diff || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -226,45 +258,19 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 14 fontSize: 14
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 14 fontSize: 14
} }
} }
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
} }
] ]
}; };

View File

@@ -87,23 +87,6 @@ export default {
sortChange(value) { sortChange(value) {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断rate对应的flag值<1为0>1为1
* @param {number} rate 处理后的rate值已*100
* @returns {0|1} flag值
*/
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;
},
/** /**
* 核心处理函数:在所有数据都准备好后,才组装 chartData * 核心处理函数:在所有数据都准备好后,才组装 chartData
@@ -118,7 +101,7 @@ getRateFlag(rate, real, target) {
const groupReal = [this.groupData.real]; // 实际值数组 const groupReal = [this.groupData.real]; // 实际值数组
const groupRate = [this.groupData.rate]; // 完成率数组 const groupRate = [this.groupData.rate]; // 完成率数组
// 新增集团rate对应的flag // 新增集团rate对应的flag
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; const groupFlag = [this.groupData.rate > 100 ? 1 : 0];
console.log('集团数据数组:', { console.log('集团数据数组:', {
groupTarget, groupTarget,
@@ -139,7 +122,7 @@ getRateFlag(rate, real, target) {
const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryRate = this.factoryData.map(item => item.rate || 0);
const factoryDiff = this.factoryData.map(item => item.diff || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0);
// 新增每个工厂rate对应的flag数组 // 新增每个工厂rate对应的flag数组
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate,item.real,item.budget)); const factoryFlags = this.factoryData.map(item => item.rate > 100 ? 1 : 0);
// 3. 组装最终的chartData供子组件使用 // 3. 组装最终的chartData供子组件使用
this.chartData = { this.chartData = {

View File

@@ -6,7 +6,6 @@
<script> <script>
import operatingLineBarSaleSingle from './operatingLineBarSaleSingle.vue'; import operatingLineBarSaleSingle from './operatingLineBarSaleSingle.vue';
import * as echarts from 'echarts';
export default { export default {
name: "Container", name: "Container",

View File

@@ -19,9 +19,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt=""> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge :id="'totalG'" :detailData="ytdData" id="totalGauge"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData"> <verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
@@ -38,9 +35,6 @@ import Container from './container.vue'
import electricityGauge from './electricityGauge.vue' import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue' import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart }, components: { Container, electricityGauge, verticalBarChart },
@@ -73,27 +67,6 @@ export default {
deep: true // 若对象内属性变化需触发,需加 deep: true 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())
},
methods: {
}
} }
</script> </script>

View File

@@ -76,7 +76,7 @@ getRateFlag(rate, real, target) {
this.myChart = echarts.init(chartDom); this.myChart = echarts.init(chartDom);
const diff = this.detailData.diff || 0 const diff = this.detailData.diff || 0
const rate = this.detailData.rate || 0 const rate = this.detailData.rate || 0
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0 const flagValue = this.detailData.rate >=100?1:0
this.flag = flagValue this.flag = flagValue
const option = { const option = {
tooltip: { tooltip: {
@@ -87,16 +87,6 @@ getRateFlag(rate, real, target) {
backgroundColor: '#6a7985' backgroundColor: '#6a7985'
} }
}, },
// formatter: (params) => {
// let html = `${params[0].axisValue}<br/>`;
// params.forEach(item => {
// const unit = item.seriesName === '完成率' ? '%' : (
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
// );
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
// });
// return html;
// }
}, },
grid: { grid: {
top: 40, top: 40,
@@ -109,9 +99,6 @@ getRateFlag(rate, real, target) {
xAxis: { xAxis: {
// 横向柱状图的x轴必须设为数值轴否则无法正常展示数值 // 横向柱状图的x轴必须设为数值轴否则无法正常展示数值
type: 'value', type: 'value',
// offset: 0,
// boundaryGap: true ,
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
axisTick: { show: false }, axisTick: { show: false },
min: 0, min: 0,
// //

View File

@@ -69,7 +69,7 @@ export default {
const data = list.find(item => item && item.title === def.name) || fallback const data = list.find(item => item && item.title === def.name) || fallback
const detailData = { const detailData = {
...data, ...data,
flag: _this.getRateFlag(data.rate, data.real, data.budget), flag: data.rate > 100 ? 1 : 0,
} }
return { return {
...def, ...def,
@@ -101,22 +101,6 @@ export default {
deep: true, deep: true,
immediate: true // 初始化立即执行 immediate: true // 初始化立即执行
} }
},
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;
}
} }
} }
</script> </script>

View File

@@ -128,10 +128,6 @@ export default {
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
}, },
methods: { methods: {
// sortChange(value) {
// this.sort = value
// this.getData()
// },
getData() { getData() {
getInventoryData({ getInventoryData({
startTime: this.dateData.startTime, startTime: this.dateData.startTime,
@@ -144,12 +140,6 @@ export default {
console.log(res); console.log(res);
this.monthData= res.data.month this.monthData= res.data.month
this.ytdData = res.data.ytd this.ytdData = res.data.ytd
// this.saleData = res.data.SaleData
// this.premiumProduct = res.data.premiumProduct
// this.salesTrendMap = res.data.salesTrendMap
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
}) })
}, },
handleTimeChange(obj) { handleTimeChange(obj) {
@@ -199,28 +189,7 @@ export default {
return false; return false;
} }
screenfull.toggle(this.$refs.dayReportB); screenfull.toggle(this.$refs.dayReportB);
}, }
// 导出
// 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> </script>

View File

@@ -1,298 +0,0 @@
<template>
<div style="flex: 1">
<Container name="数据趋势" icon="cockpitItemIcon" size="opLargeBg" topSize="large">
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%; gap: 16px">
<div class="right" style="
height: 191px;
display: flex;
width: 1595px;
background-color: rgba(249, 252, 255, 1);
">
<dataTrendBar @changeItem="handleChange" :chartData="chartData" />
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from "../components/container.vue";
import dataTrendBar from "./dataTrendBar.vue";
export default {
name: "ProductionStatus",
components: { Container, dataTrendBar },
props: {
trend: {
type: Array,
// 默认值与实际数据结构一致12个月
default: () => [
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
]
},
},
data() {
return {
chartData: {
months: [], // 月份数组2025年01月 - 2025年12月
rates: [], // 每月完成率(百分比)
reals: [], // 每月实际值
budgets: [],// 每月预算值
diffs: [], // 每月差值
flags: [] // 每月达标标识≥100 → 1<100 → 0
}
};
},
watch: {
trend: {
handler(newVal) {
this.processTrendData(newVal);
},
immediate: true,
deep: true,
},
},
mounted() {
this.processTrendData(this.trend);
},
methods: {
handleChange(value) {
this.$emit("handleChange", value);
},
/**
* 处理趋势数据适配12个月的数组结构
* @param {Array} trendData - 原始趋势数组12个月
*/
processTrendData(trendData) {
// 数据兜底确保是数组且长度为12
const validTrend = Array.isArray(trendData)
? trendData
: []
// 初始化空数组
const months = [];
const rates = [];
const reals = [];
const budgets = [];
const diffs = [];
const flags = [];
// 遍历12个月数据
validTrend.forEach(item => {
// 基础数据提取(兜底处理)
const month = item.title ?? '';
const budget = Number(item.budget) || 0;
const real = Number(item.real) || 0;
const rate = Number(item.rate) || 0;
const diff = Number(item.diff) || 0;
// 计算达标标识≥100 → 1<100 → 0
const flag = this.getRateFlag(rate, real, budget);
// 填充数组
months.push(month);
rates.push(rate); // 转为百分比并取整
reals.push(real);
budgets.push(budget);
diffs.push(diff);
flags.push(flag);
});
// 更新chartData响应式
this.chartData = {
months,
rates,
reals,
budgets,
diffs,
flags
};
console.log('处理后的趋势数据:', this.chartData);
},
/**
* 计算达标标识
* @param {Number} rate - 完成率原始值如1.2 → 120%
* @returns {Number} 1: 达标≥100%0: 未达标(<100%
*/
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;
},
},
};
</script>
<style lang="scss" scoped>
/* 滚动容器样式 */
.scroll-container {
max-height: 210px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-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 样式 */
.production-status-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.production-status-chart-tooltip * {
color: #fff !important;
}
</style>

View File

@@ -1,477 +0,0 @@
<template>
<div class="coreBar">
<!-- 新增行容器包裹各基地情况和barTop -->
<div class="header-row">
<div class="barTop">
<!-- 关键新增右侧容器包裹图例和按钮组实现整体靠右 -->
<div class="right-container">
<div class="legend">
<span class="legend-item">
<span class="legend-icon line yield"></span>
完成率
</span>
<span class="legend-item">
<span class="legend-icon square target"></span>
预算
</span>
<span class="legend-item">
<span class="legend-icon square achieved"></span>
实际·达标
</span>
<span class="legend-item">
<span class="legend-icon square unachieved"></span>
实际·未达标
</span>
</div>
<div class="button-group">
<div class="item-button category-btn">
<span class="item-text">类目选择</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="dropdown-arrow" :class="{ 'rotate': isDropdownShow }"></span>
</div>
<div class="dropdown-options" v-if="isDropdownShow">
<div class="dropdown-option" v-for="(item, index) in profitOptions" :key="index"
@click.stop="selectProfit(item)">
{{ item }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="lineBottom" style="height: 100%; width: 100%">
<operatingLineBar :chartData="chartD" style="height: 99%; width: 100%" />
</div>
</div>
</template>
<script>
import operatingLineBar from './operatingLineBarSale.vue';
import * as echarts from 'echarts';
export default {
name: "Container",
components: { operatingLineBar },
props: ["chartData"],
data() {
return {
activeButton: 0,
isDropdownShow: false,
selectedProfit: '营业收入', // 选中的名称初始为null
profitOptions: [
'营业收入',
'单价',
'销量',
]
};
},
computed: {
// profitOptions() {
// return this.categoryData.map(item => item.name) || [];
// },
currentDataSource() {
console.log('yyyy', this.chartData);
return this.chartData
},
locations() {
console.log('this.chartData', this.chartData);
return this.chartData.months
},
// 根据按钮切换生成对应的 chartData
chartD() {
const data = this.currentDataSource;
console.log(this.currentDataSource, 'currentDataSource');
const salesData = {
allPlaceNames: this.locations,
series: [
// 1. 完成率(折线图)
{
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(40, 138, 255, 0)' }
])
},
data: data.rates, // 完成率(%
symbol: 'circle',
symbolSize: 6
},
// 2. 目标(柱状图)
{
name: '预算',
type: 'bar',
yAxisIndex: 0, // 左侧Y轴万元
barWidth: 14,
itemStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
]
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.budgets // 目标销量(万元)
},
// 3. 实际(柱状图,含达标状态)
{
name: '实际',
type: 'bar',
yAxisIndex: 0,
barWidth: 14,
label: {
show: true,
position: 'top',
offset: [0, 0],
// 固定label尺寸68px×20px
width: 68,
height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸
formatter: (params) => {
const diff = data.diffs || [];
const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0;
const currentFlag = flags[params.dataIndex] || 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: {
color: (params) => {
// 达标状态1=达标绿色0=未达标(橙色)
const safeFlag = data.flags;
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals // 实际销量(万元)
}
]
};
// 根据按钮状态返回对应数据
return salesData;
}
},
methods: {
selectProfit(item) {
this.selectedProfit = item;
this.isDropdownShow = false;
this.$emit("changeItem", item);
}
},
};
</script>
<style scoped lang="scss">
.coreBar {
display: flex;
flex-direction: column;
width: 100%;
padding: 12px;
// 新增:头部行容器,实现一行排列
.header-row {
display: flex;
justify-content: flex-end; // 左右两端对齐
align-items: center; // 垂直居中
// width: 100%;
margin-bottom: 8px; // 与下方图表区保留间距(可根据需求调整)
}
// 各基地情况标题样式
.base-title {
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
font-style: normal;
padding: 0 0 0 16px; // 保留原有内边距
white-space: nowrap; // 防止文字换行
}
.barTop {
// 移除原有flex和justify-content由header-row控制
width: auto; // 自适应宽度
// 保留原有align-items确保内部元素垂直居中
align-items: center;
gap: 16px;
// 1. 右侧容器:包裹图例和按钮组,整体靠右
.right-container {
display: flex;
align-items: center; // 图例和按钮组垂直居中
gap: 24px; // 图例与按钮组的间距,避免贴紧
margin-right: 46px; // 右侧整体留边,与原按钮组边距一致
}
// 2. 图例:在右侧容器内横向排列
.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);
}
// 3. 按钮组:在右侧容器内,保留原有样式
.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;
overflow: hidden;
.item-text {
display: inline-block;
}
}
.category-btn {
width: 75px;
border-top-left-radius: 12px;
border-bottom-left-radius: 12px;
background: #ffffff;
color: #0b58ff;
text-align: center;
}
.profit-btn {
width: 123px;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
position: relative;
padding: 0 18px 0 8px;
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: 123px;
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>

View File

@@ -1,204 +0,0 @@
<template>
<div style="flex: 1">
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
<!-- 1. 移除 .kpi-content 的固定高度改为自适应 -->
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<!-- 新增topItem 专属包裹容器统一控制样式和布局 -->
<div class="topItem-container" style="display: flex; gap: 8px;">
<div class="dashboard">
<div class="title">
{{ month }}月完成率
</div>
<div class="number">
<div class="yield">
{{ monthData?.rate || 0 }}%
</div>
<div class="mom">
环比{{ monthData?.momRate }}%
<img v-if="monthData?.momRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div>
</div>
<!-- <div class="electricityGauge">
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
</div> -->
</div>
<div class="line" style="padding: 0px;">
<verticalBarChart :detailData="monthData">
</verticalBarChart>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default {
name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart },
// mixins: [resize],
props: {
monthData: { // 接收父组件传递的设备数据数组
type: Object,
default: () => {} // 默认空数组,避免报错
},
title: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
month: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
},
data() {
return {
chart: null,
}
},
watch: {
// itemData: {
// handler(newValue, oldValue) {
// // this.updateChart()
// },
// 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())
},
methods: {
}
}
</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;
}
.dashboard {
width: 264px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
.title {
// width: 190px;
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;
letter-spacing: 2px;
}
.number {
font-family: YouSheBiaoTiHei;
font-size: 46px;
color: #0B58FF;
letter-spacing: 2px;
text-align: center;
font-style: normal;
white-space: nowrap;
margin-top: 20px;
}
.mom {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: center;
font-style: normal;
margin-top: 20px;
}
}
.line {
width: 500px;
height: 205px;
background: #F9FCFF;
}
// .leftTitle {
// .item {
// width: 67px;
// height: 180px;
// padding: 37px 23px;
// background: #F9FCFF;
// font-family: PingFangSC, PingFang SC;
// font-weight: 400;
// font-size: 18px;
// color: #000000;
// line-height: 25px;
// letter-spacing: 1px;
// // text-align: left;
// font-style: normal;
// }
// }
</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> -->

View File

@@ -1,221 +0,0 @@
<template>
<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%;">
<!-- 销量模块直接传递整合了flag的salesData -->
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
<div style='position: relative;'>
<div class="title">
销量·
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[0].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[0].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
</div>
</div>
<!-- 单价模块直接传递整合了flag的unitPriceData -->
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
<div style='position: relative;'>
<div class="title">
单价·/
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[1].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[1].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
</div>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
export default {
name: 'ProductionStatus',
components: { Container, operatingSingleBar },
props: {
monthAnalysis: {
type: Array,
default: () => [
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
]
},
dateData: {
type: Object,
default: () => {}
},
title: {
type: String,
default: ''
},
factory: {
type: [String,Number],
default: ''
},
month: {
type: String,
default: ''
},
},
data() {
return {
chart: null,
// 初始化数据包含flag字段
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
}
},
watch: {
monthAnalysis: {
handler(newVal) {
this.updateChart(newVal)
},
deep: true,
immediate: true
}
},
mounted() {
this.updateChart(this.monthAnalysis)
},
methods: {
handleDashboardClick(path) {
this.$router.push({
path: path,
query: {
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
dateData: this.dateData
}
})
},
// 判断flag的核心方法
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;
},
updateChart(data) {
// 数据兜底
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
// 核心修改将flag整合到数据对象中无需单独定义salesFlag/unitPriceFlag
this.salesData = {
...salesItem, // 合并原有字段
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
};
this.unitPriceData = {
...unitPriceItem, // 合并原有字段
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
};
// 调试:确认整合后的数据
console.log('整合flag后的销量数据', this.salesData);
console.log('整合flag后的单价数据', this.unitPriceData);
}
}
}
</script>
<style lang='scss' scoped>
.scroll-container {
max-height: 210px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
.topItem-container {
display: flex;
justify-content: space-between;
}
.dashboard {
flex: 1;
min-width: 300px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
margin: 0 4px;
.title {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 2px;
text-align: left;
margin-bottom: 12px;
}
.chart-wrap {
width: 100%;
height: calc(100% - 30px);
}
.number {
display: flex;
align-items: center;
gap: 30px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
}
.mom {
width: 97px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
z-index: 1000;
}
}
.dashboard.left {
margin-left: 0;
}
.dashboard.right {
margin-right: 0;
}
</style>

View File

@@ -173,26 +173,62 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 40, barWidth: 40,
label: {
show: false
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
},
// 实际差值标签独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: {
show: false
},
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [32, 0], offset: [32, 0],
width: 100, width: 100,
height: 22, height: 22,
formatter: (params) => { formatter: () => {
const diff = data.diff || []; const diff = data.diff || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '-';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -225,45 +261,19 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 14, fontSize: 14,
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 14, fontSize: 14,
} }
} }
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
} }
] ]
}; };

View File

@@ -32,7 +32,6 @@
width: 1220px; width: 1220px;
background-color: rgba(249, 252, 255, 1); background-color: rgba(249, 252, 255, 1);
"> ">
<!-- <top-item /> -->
<operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" /> <operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" />
</div> </div>
</div> </div>
@@ -88,23 +87,6 @@ export default {
sortChange(value) { sortChange(value) {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断rate对应的flag值<1为0>1为1
* @param {number} rate 处理后的rate值已*100
* @returns {0|1} flag值
*/
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;
},
/** /**
* 核心处理函数:在所有数据都准备好后,才组装 chartData * 核心处理函数:在所有数据都准备好后,才组装 chartData
*/ */
@@ -118,7 +100,7 @@ getRateFlag(rate, real, target) {
const groupReal = [this.groupData.real]; // 实际值数组 const groupReal = [this.groupData.real]; // 实际值数组
const groupRate = [this.groupData.rate]; // 完成率数组 const groupRate = [this.groupData.rate]; // 完成率数组
// 新增集团rate对应的flag // 新增集团rate对应的flag
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; const groupFlag = [this.groupData.rate >= 100 ? 1 : 0];
console.log('集团数据数组:', { console.log('集团数据数组:', {
groupTarget, groupTarget,
@@ -139,7 +121,7 @@ getRateFlag(rate, real, target) {
const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryRate = this.factoryData.map(item => item.rate || 0);
const factoryDiff = this.factoryData.map(item => item.diff || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0);
// 新增每个工厂rate对应的flag数组 // 新增每个工厂rate对应的flag数组
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
// 3. 组装最终的chartData供子组件使用 // 3. 组装最终的chartData供子组件使用
this.chartData = { this.chartData = {

View File

@@ -1,202 +0,0 @@
<template>
<div style="flex: 1">
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
<!-- 1. 移除 .kpi-content 的固定高度改为自适应 -->
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<!-- 新增topItem 专属包裹容器统一控制样式和布局 -->
<div class="topItem-container" style="display: flex; gap: 8px;">
<div class="dashboard">
<div class="title">
累计完成率
</div>
<div class="number">
<div class="yield">
{{ ytdData?.rate || 0}}%
</div>
<div class="mom">
同比{{ ytdData?.yoyRate || 0}}%
<img v-if="ytdData?.yoyRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div>
</div>
<!-- <div class="electricityGauge">
<electricityGauge :id=" 'totalG' " :detailData="ytdData" id="totalGauge"></electricityGauge>
</div> -->
</div>
<div class="line" style="padding: 0px;">
<verticalBarChart :refName=" 'totalVerticalBarChart' " :detailData="ytdData">
</verticalBarChart>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default {
name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart },
// mixins: [resize],
props: {
ytdData: { // 接收父组件传递的设备数据数组
type: Object,
default: () => {} // 默认空数组,避免报错
},
title: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
month: { // 接收父组件传递的设备数据数组
type: String,
default: () => '' // 默认空数组,避免报错
},
},
data() {
return {
chart: null,
}
},
watch: {
itemData: {
handler(newValue, oldValue) {
// this.updateChart()
},
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())
},
methods: {
}
}
</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;
}
.dashboard {
width: 264px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
.title {
// width: 190px;
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;
letter-spacing: 2px;
}
.number {
font-family: YouSheBiaoTiHei;
font-size: 46px;
color: #0B58FF;
letter-spacing: 2px;
text-align: center;
font-style: normal;
margin-top: 20px;
}
.mom {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: center;
font-style: normal;
margin-top: 20px;
}
}
.line {
width: 500px;
height: 205px;
background: #F9FCFF;
}
// .leftTitle {
// .item {
// width: 67px;
// height: 180px;
// padding: 37px 23px;
// background: #F9FCFF;
// font-family: PingFangSC, PingFang SC;
// font-weight: 400;
// font-size: 18px;
// color: #000000;
// line-height: 25px;
// letter-spacing: 1px;
// // text-align: left;
// font-style: normal;
// }
// }
</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> -->

View File

@@ -1,226 +0,0 @@
<template>
<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:detailData.flags>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';
export default {
components: {},
data() {
return {
myChart: null // 存储图表实例,避免重复创建
};
},
props: {
// 明确接收的props结构增强可读性
refName: {
type: String,
default: () => 'verticalBarChart',
},
detailData: {
type: Object,
default: () => ({
}),
}
},
mounted() {
this.$nextTick(() => {
this.updateChart();
});
},
// 新增:监听 chartData 变化
watch: {
// 深度监听数据变化,仅更新图表配置(不销毁实例)
detailData: {
handler() {
console.log(this.chartData, 'chartData');
this.updateChart();
},
deep: true,
immediate: true // 初始化时立即执行
}
},
methods: {
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;
},
updateChart() {
const chartDom = this.$refs[this.refName];
if (!chartDom) {
console.error('图表容器未找到!');
return;
}
if (this.myChart) {
this.myChart.dispose();
}
this.myChart = echarts.init(chartDom);
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
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
},
// formatter: (params) => {
// let html = `${params[0].axisValue}<br/>`;
// params.forEach(item => {
// const unit = item.seriesName === '完成率' ? '%' : (
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
// );
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
// });
// return html;
// }
},
grid: {
top: 40,
bottom: 15,
right: 80,
left: 10,
containLabel: true,
show: false // 隐藏grid背景避免干扰
},
xAxis: {
// 横向柱状图的x轴必须设为数值轴否则无法正常展示数值
type: 'value',
// offset: 0,
// boundaryGap: true ,
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
axisTick: { show: false },
min: 0,
//
splitNumber: 4,
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: xData // 数值轴不需要手动设置data由series的数据自动生成
},
yAxis: {
type: 'category',
axisLabel: {
color: 'rgba(0, 0, 0, 0.75)',
fontSize: 12,
interval: 0,
padding: [5, 0, 0, 0]
},
axisLine: {
show: true, // 显示Y轴轴线关键
lineStyle: {
color: '#E5E6EB', // 轴线颜色(浅灰色,可自定义)
width: 1, // 轴线宽度
type: 'solid' // 实线可选dashed虚线、dotted点线
}
},
axisTick: { show: false },
// padding: [300, 100, 100, 100],
data: ['实际', '预算'] // y轴分类实际、预算
},
series: [
{
// name: '预算',
type: 'bar',
barWidth: 24,
// barCategoryGap: '50', // 柱子之间的间距(相对于柱子宽度)
// 数据长度与yAxis的分类数量匹配实际、预算各一个值
data: [{
value: this.detailData.real,
label: {
show: true,
position: 'right',
fontSize: 14,
},
itemStyle: {
color: flagValue === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
},
borderRadius: [4, 4, 0, 0]
}
}, {
value: this.detailData.target,
label: {
show: true,
position: 'right',
fontSize: 14,
},
itemStyle: {
// 预算的渐变颜色(蓝系渐变)
color: {
type: 'linear',
x: 1, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: '#82CCFF' }, // 浅蓝
{ offset: 1, color: '#4B9DFF' } // 深蓝
]
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
},],
},
]
};
option && this.myChart.setOption(option);
// 窗口缩放适配和销毁逻辑保持不变
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();
});
}
},
};
</script>

View File

@@ -1,217 +0,0 @@
<template>
<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%;">
<!-- 销量模块直接传递整合了flag的salesData -->
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
<div style='position: relative;'>
<div class="title">
销量·
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[0].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[0].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
</div>
</div>
<!-- 单价模块直接传递整合了flag的unitPriceData -->
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
<div style='position: relative;'>
<div class="title">
单价·/
</div>
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[1].rate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[1].diff}}</span></span>
</div>
</div>
<div class="chart-wrap">
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
</div>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
export default {
name: 'ProductionStatus',
components: { Container, operatingSingleBar },
props: {
ytdAnalysis: {
type: Array,
default: () => [
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
]
},
dateData: {
type: Object,
default: () => {}
},
title: {
type: String,
default: ''
},
month: {
type: String,
default: ''
},
},
data() {
return {
chart: null,
// 初始化数据包含flag字段
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
}
},
watch: {
ytdAnalysis: {
handler(newVal) {
this.updateChart(newVal)
},
deep: true,
immediate: true
}
},
mounted() {
this.updateChart(this.ytdAnalysis)
},
methods: {
handleDashboardClick(path) {
this.$router.push({
path: path,
query: {
factory: this.$route.query.factory ? this.$route.query.factory : 5,
dateData: this.dateData
}
})
},
// 判断flag的核心方法
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;
},
updateChart(data) {
// 数据兜底
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
// 核心修改将flag整合到数据对象中无需单独定义salesFlag/unitPriceFlag
this.salesData = {
...salesItem, // 合并原有字段
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
};
this.unitPriceData = {
...unitPriceItem, // 合并原有字段
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
};
// 调试:确认整合后的数据
console.log('整合flag后的销量数据', this.salesData);
console.log('整合flag后的单价数据', this.unitPriceData);
}
}
}
</script>
<style lang='scss' scoped>
.scroll-container {
max-height: 210px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
.topItem-container {
display: flex;
justify-content: space-between;
}
.dashboard {
flex: 1;
min-width: 300px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
margin: 0 4px;
.title {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 2px;
text-align: left;
margin-bottom: 12px;
}
.chart-wrap {
width: 100%;
height: calc(100% - 30px);
}
.number {
display: flex;
align-items: center;
gap: 30px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
}
.mom {
width: 97px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
z-index: 1000;
}
}
.dashboard.left {
margin-left: 0;
}
.dashboard.right {
margin-right: 0;
}
</style>

View File

@@ -26,39 +26,26 @@
grid-template-columns: 1624px; grid-template-columns: 1624px;
"> ">
<operatingLineChartCumulative :dateData="dateData" :totalData="totalData" /> <operatingLineChartCumulative :dateData="dateData" :totalData="totalData" />
<!-- <keyWork /> -->
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
import ReportHeader from "../components/noRouterHeader.vue"; import ReportHeader from "../components/noRouterHeader.vue";
import { Sidebar } from "../../../layout/components"; import { Sidebar } from "../../../layout/components";
import screenfull from "screenfull"; import screenfull from "screenfull";
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
// import premProdStatus from "./components/premProdStatus.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import operatingLineChart from "../netPriceAnalysisComponents/operatingLineChart"; import operatingLineChart from "../netPriceAnalysisComponents/operatingLineChart";
import operatingLineChartCumulative from "../netPriceAnalysisComponents/operatingLineChartCumulative.vue"; import operatingLineChartCumulative from "../netPriceAnalysisComponents/operatingLineChartCumulative.vue";
import { getUnitPriceAnalysisGroupData } from '@/api/cockpit' import { getUnitPriceAnalysisGroupData } from '@/api/cockpit'
import moment from "moment";
export default { export default {
name: "DayReport", name: "DayReport",
components: { components: {
ReportHeader, ReportHeader,
operatingLineChartCumulative, operatingLineChartCumulative,
operatingLineChart, operatingLineChart,
// premProdStatus,
Sidebar, Sidebar,
}, },
data() { data() {
@@ -149,12 +136,6 @@ export default {
console.log(res); console.log(res);
this.thisMonData = res.data.thisMonData this.thisMonData = res.data.thisMonData
this.totalData = res.data.totalData this.totalData = res.data.totalData
// this.saleData = res.data.SaleData
// this.premiumProduct = res.data.premiumProduct
// this.salesTrendMap = res.data.salesTrendMap
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
}) })
}, },
handleTimeChange(obj) { handleTimeChange(obj) {
@@ -204,28 +185,7 @@ export default {
return false; return false;
} }
screenfull.toggle(this.$refs.dayReportB); screenfull.toggle(this.$refs.dayReportB);
}, }
// 导出
// 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> </script>

View File

@@ -52,14 +52,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div> </div>
</template> </template>
<script> <script>
@@ -69,21 +61,12 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue"; import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../netPriceAnalysisComponents/monthlyOverview.vue"; import monthlyOverview from "../netPriceAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../netPriceAnalysisComponents/totalOverview.vue"; import totalOverview from "../netPriceAnalysisComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
import monthlyRelatedMetrics from "../netPriceAnalysisComponents/monthlyRelatedMetrics.vue"; import monthlyRelatedMetrics from "../netPriceAnalysisComponents/monthlyRelatedMetrics.vue";
import yearRelatedMetrics from "../netPriceAnalysisComponents/yearRelatedMetrics.vue"; import yearRelatedMetrics from "../netPriceAnalysisComponents/yearRelatedMetrics.vue";
import dataTrend from "../netPriceAnalysisComponents/dataTrend.vue"; import dataTrend from "../netPriceAnalysisComponents/dataTrend.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
import { getUnitPriceAnalysisBaseData } from '@/api/cockpit' import { getUnitPriceAnalysisBaseData } from '@/api/cockpit'
// import PSDO from "./components/PSDO.vue";
// import psiLineChart from "./components/psiLineChart.vue";
// import coreBottomLeft from "./components/coreBottomLeft.vue";
// import orderProgress from "./components/orderProgress.vue";
// import keyWork from "./components/keyWork.vue";
import moment from "moment"; import moment from "moment";
// import html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default { export default {
name: "DayReport", name: "DayReport",
components: { components: {
@@ -95,7 +78,6 @@ export default {
monthlyRelatedMetrics, monthlyRelatedMetrics,
yearRelatedMetrics, yearRelatedMetrics,
dataTrend dataTrend
// psiLineChart
}, },
data() { data() {
return { return {
@@ -275,35 +257,12 @@ export default {
}, },
changeDate(val) { changeDate(val) {
this.date = val; this.date = val;
// this.weekDay = this.weekArr[moment(this.date).format('e')]
// this.getData()
if (this.date === moment().format("yyyy-MM-DD")) { if (this.date === moment().format("yyyy-MM-DD")) {
this.loopTime(); this.loopTime();
} else { } else {
clearInterval(this.timer); clearInterval(this.timer);
} }
}, }
// 导出
// () {
// 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> </script>

View File

@@ -97,24 +97,11 @@ export default {
rates.push(data.completeRate || 0); rates.push(data.completeRate || 0);
reals.push(data.real || 0); reals.push(data.real || 0);
targets.push(data.target || 0); targets.push(data.target || 0);
// 复用 getRateFlag 逻辑 flags.push(data.completeRate>=100?1:0);
flags.push(this.getRateFlag(data.completeRate, data.real, data.target));
}); });
return { rates, reals, targets, flags }; return { rates, reals, targets, flags };
}, },
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;
},
} }
}; };
</script> </script>

View File

@@ -101,7 +101,7 @@ export default {
targets.push(monthData.target || 0); targets.push(monthData.target || 0);
diffs.push(monthData.diff || 0); diffs.push(monthData.diff || 0);
// 生成达标状态(复用 getRateFlag 逻辑) // 生成达标状态(复用 getRateFlag 逻辑)
flags.push(this.getRateFlag(completeRate, monthData.real, monthData.target)); flags.push(completeRate>=100?1:0);
}); });
return { return {
@@ -176,29 +176,55 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 14, barWidth: 14,
label: { show: false },
itemStyle: {
color: (params) => {
const currentFlag = flags[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: reals, // 动态实际值
},
// 实际柱状图的标签层独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: { show: false },
data: (reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [0, 0], offset: [0, 0],
// 固定label尺寸68px×20px
width: 68, width: 68,
height: 20, height: 20,
// 关键:去掉换行,让文字在一行显示,适配小尺寸 formatter: () => {
formatter: (params) => { const currentDiff = diffs[index] || 0;
const currentFlag = flags[index] || 0;
// const flags = flags || [];
const currentDiff = diffs[params.dataIndex] || 0;
const currentFlag = flags[params.dataIndex] || 0;
console.log('flags[params.dataIndex]', flags);
const prefix = currentFlag === 1 ? '+' : '';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -232,46 +258,21 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 11, fontSize: 11,
lineHeight: 20 lineHeight: 20
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 11, fontSize: 11,
lineHeight: 20 lineHeight: 20
} }
} }
},
itemStyle: {
color: (params) => {
const currentFlag = flags[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: reals, // 动态实际值
} }
] ]
}; };
@@ -284,19 +285,6 @@ export default {
selectProfit(item) { selectProfit(item) {
this.selectedProfit = item; this.selectedProfit = item;
this.isDropdownShow = false; this.isDropdownShow = false;
},
// 复用达标状态判断方法
getRateFlag(rate, real, target) {
// 先处理无效值的情况
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 实际值和目标值都为0时算作达标
if (real === 0 && target === 0 && rate === 0) {
return 1; // 达标
}
// 其他情况rate >= 100 或 rate === 0 时达标
return (rate >= 100) ? 1 : 0;
} }
} }
}; };

View File

@@ -17,9 +17,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降"> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge id="month" :detailData="factoryData"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<!-- 传递包含flag的factoryData给柱状图组件 --> <!-- 传递包含flag的factoryData给柱状图组件 -->
@@ -86,7 +83,7 @@ export default {
target: 0, target: 0,
thb: 0, thb: 0,
...rawData, ...rawData,
flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段 flag: rawData.completeRate >= 100 ? 1 : 0 // 根据完成率计算flag
}; };
} }
}, },
@@ -100,23 +97,6 @@ export default {
} }
return value; return value;
}, },
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如89代表89%
* @returns {0|1} flag值
*/
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;
},
} }
} }
</script> </script>

View File

@@ -29,9 +29,6 @@
import Container from './container.vue' import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue' import operatingSingleBar from './operatingSingleBar.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, operatingSingleBar }, components: { Container, operatingSingleBar },
@@ -92,7 +89,7 @@ export default {
const data = list.find(item => item && item.title === def.name) || fallback const data = list.find(item => item && item.title === def.name) || fallback
const detailData = { const detailData = {
...data, ...data,
flag: _this.getRateFlag((data || _this.defaultData).completeRate, (data || _this.defaultData).real, (data || _this.defaultData).target), flag: data.completeRate >= 100 ? 1 : 0,
} }
return { return {
...def, ...def,
@@ -140,23 +137,6 @@ export default {
} }
}) })
}, },
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如89代表89%
* @returns {0|1} flag值
*/
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;
},
/** /**
* 图表更新方法:可在这里补充全局的图表刷新逻辑 * 图表更新方法:可在这里补充全局的图表刷新逻辑
@@ -164,10 +144,6 @@ getRateFlag(rate, real, target) {
*/ */
updateChart() { updateChart() {
console.log('数据更新当前relatedMon:', this.relatedMon) console.log('数据更新当前relatedMon:', this.relatedMon)
// 打印各维度的flag值方便调试
// console.log('销量flag:', this.getRateFlag(this.relatedMon.销量.completeRate))
// console.log('成本flag:', this.getRateFlag(this.relatedMon.成本.completeRate))
// console.log('运费flag:', this.getRateFlag(this.relatedMon.运费.completeRate))
} }
} }
} }

View File

@@ -173,26 +173,58 @@ export default {
type: 'bar', type: 'bar',
yAxisIndex: 0, yAxisIndex: 0,
barWidth: 40, barWidth: 40,
label: { show: false },
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
}
: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
},
// 实际柱状图的标签层独立scatter系列zlevel=1确保标签在最上层
{
name: '__实际差值标签',
type: 'scatter',
yAxisIndex: 0,
zlevel: 1,
symbolSize: 0,
tooltip: { show: false },
data: (data.reals || []).map((value, index) => ({
value: value,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
offset: [32, 0], offset: [32, 0],
width: 100, width: 100,
height: 22, height: 22,
formatter: (params) => { formatter: () => {
const diff = data.diff || []; const diff = data.diff || [];
const flags = data.flags || []; const flags = data.flags || [];
const currentDiff = diff[params.dataIndex] || 0; const currentDiff = diff[index] || 0;
const currentFlag = flags[params.dataIndex] || 0; const currentFlag = flags[index] || 0;
const prefix = currentFlag === 1 ? '+' : '';
// 根据标志位选择不同的样式类
if (currentFlag === 1) { if (currentFlag === 1) {
// 达标 - 使用 rate-achieved 样式
return `{achieved|${currentDiff}}{text|差值}`; return `{achieved|${currentDiff}}{text|差值}`;
} else { } else {
// 未达标 - 使用 rate-unachieved 样式
return `{unachieved|${currentDiff}}{text|差值}`; return `{unachieved|${currentDiff}}{text|差值}`;
} }
}, },
@@ -225,45 +257,19 @@ export default {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#76DABE', // 与达标的 offset: 1 颜色一致 color: '#76DABE',
fontSize: 14 fontSize: 14
}, },
// 未达标样式
unachieved: { unachieved: {
width: 'auto', width: 'auto',
padding: [5, 0, 5, 10], padding: [5, 0, 5, 10],
align: 'center', align: 'center',
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致 color: '#F9A44A',
fontSize: 14 fontSize: 14
} }
} }
},
itemStyle: {
color: (params) => {
const safeFlag = data.flags || [];
const currentFlag = safeFlag[params.dataIndex] || 0;
return currentFlag === 1
? {
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)' }
]
} }
: { }))
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
]
};
},
borderRadius: [4, 4, 0, 0],
borderWidth: 0
},
data: data.reals || []
} }
] ]
}; };

View File

@@ -80,24 +80,6 @@ export default {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如80代表80%
* @returns {0|1} flag值
*/
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;
},
/** /**
* 核心处理函数解析thisMonData组装集团和工厂数据 * 核心处理函数解析thisMonData组装集团和工厂数据
*/ */
@@ -116,7 +98,7 @@ getRateFlag(rate, real, target) {
diff: [ksxnData.diff], diff: [ksxnData.diff],
reals: [ksxnData.real], reals: [ksxnData.real],
rate: [ksxnData.completeRate], rate: [ksxnData.completeRate],
flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)], flags: [ksxnData.completeRate >= 100 ? 1 : 0],
thb: [ksxnData.thb] // 新增thb字段如果子组件需要 thb: [ksxnData.thb] // 新增thb字段如果子组件需要
}; };
@@ -131,7 +113,7 @@ getRateFlag(rate, real, target) {
diff: factoryDataList.map(item => item.diff || 0), // 差值 diff: factoryDataList.map(item => item.diff || 0), // 差值
reals: factoryDataList.map(item => item.real || 0), // 实际值 reals: factoryDataList.map(item => item.real || 0), // 实际值
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率 rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识 flags: factoryDataList.map(item => item.completeRate>=100?1:0), // 完成率标识
thb: factoryDataList.map(item => item.thb || 0) // thb字段 thb: factoryDataList.map(item => item.thb || 0) // thb字段
}; };

View File

@@ -79,25 +79,6 @@ export default {
sortChange(value) { sortChange(value) {
this.$emit('sort-change', value); this.$emit('sort-change', value);
}, },
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如80代表80%
* @returns {0|1} flag值
*/
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;
},
/** /**
* 核心处理函数解析thisMonData组装集团和工厂数据 * 核心处理函数解析thisMonData组装集团和工厂数据
*/ */
@@ -116,7 +97,7 @@ getRateFlag(rate, real, target) {
diff: [ksxnData.diff], diff: [ksxnData.diff],
reals: [ksxnData.real], reals: [ksxnData.real],
rate: [ksxnData.completeRate], rate: [ksxnData.completeRate],
flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)], flags: [ksxnData.completeRate >= 100 ? 1 : 0],
thb: [ksxnData.thb] // 新增thb字段如果子组件需要 thb: [ksxnData.thb] // 新增thb字段如果子组件需要
}; };
@@ -131,7 +112,7 @@ getRateFlag(rate, real, target) {
diff: factoryDataList.map(item => item.diff || 0), // 差值 diff: factoryDataList.map(item => item.diff || 0), // 差值
reals: factoryDataList.map(item => item.real || 0), // 实际值 reals: factoryDataList.map(item => item.real || 0), // 实际值
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率 rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识 flags: factoryDataList.map(item => item.completeRate >= 100 ? 1 : 0), // 完成率标识
thb: factoryDataList.map(item => item.thb || 0) // thb字段 thb: factoryDataList.map(item => item.thb || 0) // thb字段
}; };

View File

@@ -19,9 +19,6 @@
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt=""> <img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
</div> </div>
</div> </div>
<!-- <div class="electricityGauge">
<electricityGauge id="year" :detailData="factoryData"></electricityGauge>
</div> -->
</div> </div>
<div class="line" style="padding: 0px;"> <div class="line" style="padding: 0px;">
<verticalBarChart :detailData="factoryData"> <verticalBarChart :detailData="factoryData">
@@ -38,9 +35,6 @@ import Container from './container.vue'
import electricityGauge from './electricityGauge.vue' import electricityGauge from './electricityGauge.vue'
import verticalBarChart from './verticalBarChart.vue' import verticalBarChart from './verticalBarChart.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, electricityGauge, verticalBarChart }, components: { Container, electricityGauge, verticalBarChart },
@@ -90,7 +84,7 @@ export default {
target: 0, target: 0,
thb: 0, thb: 0,
...rawData, ...rawData,
flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段 flag: rawData.completeRate >= 100 ? 1 : 0 // 根据完成率计算flag
}; };
} }
}, },
@@ -103,24 +97,7 @@ export default {
return 0; return 0;
} }
return value; return value;
}, }
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如89代表89%
* @returns {0|1} flag值
*/
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;
},
} }
} }
</script> </script>

View File

@@ -29,9 +29,6 @@
import Container from './container.vue' import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue' import operatingSingleBar from './operatingSingleBar.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, operatingSingleBar }, components: { Container, operatingSingleBar },
@@ -92,7 +89,7 @@ export default {
const data = list.find(item => item && item.title === def.name) || fallback const data = list.find(item => item && item.title === def.name) || fallback
const detailData = { const detailData = {
...data, ...data,
flag: _this.getRateFlag((data || _this.defaultData).completeRate, (data || _this.defaultData).real, (data || _this.defaultData).target), flag: data.completeRate >= 100 ? 1 : 0,
} }
return { return {
...def, ...def,
@@ -140,24 +137,6 @@ export default {
} }
}) })
}, },
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如89代表89%
* @returns {0|1} flag值
*/
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;
},
/** /**
* 图表更新方法:可在这里补充全局的图表刷新逻辑 * 图表更新方法:可在这里补充全局的图表刷新逻辑
* 若子组件内部已监听 chartData 变化,此方法可留空或补充额外逻辑 * 若子组件内部已监听 chartData 变化,此方法可留空或补充额外逻辑

Some files were not shown because too many files have changed in this diff Show More