From b76f3bfd3707ca02848f81fb7b5c1b32bebbe0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=98937886381=E2=80=99?= <‘937886381@qq.com’> Date: Wed, 7 Jan 2026 16:47:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/icons/svg/closeSider.svg | 4 + src/assets/icons/svg/openSider.svg | 12 + src/views/home/components/Header.vue | 175 +++++-------- src/views/home/components/coresBar.vue | 18 +- src/views/home/components/noRouterHeader.vue | 233 ++++++------------ src/views/home/components/productBar.vue | 23 +- src/views/home/components/purchase-Item.vue | 90 ++++++- .../expenseAnalysisComponents/dataTrend.vue | 18 +- .../dataTrendBar.vue | 27 +- .../monthlyRelatedMetrics.vue | 23 +- .../operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 26 +- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 18 +- .../operatingSingleBar.vue | 67 +++-- .../operatingTopBar.vue | 55 +++-- .../verticalBarChart.vue | 77 +++--- .../yearRelatedMetrics.vue | 22 +- .../dataTrendBar.vue | 61 +++-- .../monthlyOverview.vue | 18 +- .../operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 29 ++- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 20 +- .../operatingSingleBar.vue | 69 ++++-- .../operatingTopBar.vue | 55 +++-- .../relatedIndicatorsAnalysis.vue | 26 +- .../totalOverview.vue | 18 +- .../verticalBarChart.vue | 46 +++- .../home/grossMarginComponents/dataTrend.vue | 22 +- .../grossMarginComponents/dataTrendBar.vue | 137 +++------- .../monthlyRelatedMetrics.vue | 20 +- .../grossMarginComponents/operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 40 ++- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 20 +- .../operatingSingleBar.vue | 70 ++++-- .../grossMarginComponents/operatingTopBar.vue | 58 +++-- .../verticalBarChart.vue | 80 +++--- .../yearRelatedMetrics.vue | 20 +- src/views/home/index.vue | 54 ++-- .../inputOutputRatioComponents/dataTrend.vue | 20 +- .../dataTrendBar.vue | 55 +++-- .../monthlyRelatedMetrics.vue | 20 +- .../operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 27 +- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 20 +- .../operatingSingleBar.vue | 70 ++++-- .../operatingTopBar.vue | 58 +++-- .../verticalBarChart.vue | 80 +++--- .../yearRelatedMetrics.vue | 20 +- .../netPriceAnalysisComponents/dataTrend.vue | 18 +- .../dataTrendBar.vue | 74 ++++-- .../monthlyOverview.vue | 18 +- .../monthlyRelatedMetrics.vue | 20 +- .../operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 30 ++- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 20 +- .../operatingSingleBar.vue | 70 ++++-- .../operatingTopBar.vue | 58 +++-- .../totalOverview.vue | 18 +- .../verticalBarChart.vue | 44 +++- .../yearRelatedMetrics.vue | 25 +- .../home/operatingComponents/dataTrend.vue | 20 +- .../home/operatingComponents/dataTrendBar.vue | 55 +++-- .../monthlyRelatedMetrics.vue | 20 +- .../home/operatingComponents/operatingBar.vue | 35 ++- .../operatingLineBarSale.vue | 26 +- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 20 +- .../operatingSingleBar.vue | 70 ++++-- .../operatingComponents/operatingTopBar.vue | 58 +++-- .../operatingComponents/verticalBarChart.vue | 80 +++--- .../yearRelatedMetrics.vue | 20 +- .../dataTrendBar.vue | 55 +++-- .../monthlyOverview.vue | 6 +- .../operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 29 ++- .../operatingSingleBar.vue | 70 ++++-- .../operatingTopBar.vue | 58 +++-- .../totalOverview.vue | 6 +- .../verticalBarChart.vue | 44 +++- .../dataTrendBar.vue | 59 +++-- .../monthlyOverview.vue | 18 +- .../operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 26 +- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 20 +- .../operatingSingleBar.vue | 70 ++++-- .../operatingTopBar.vue | 58 +++-- .../totalOverview.vue | 18 +- .../verticalBarChart.vue | 44 +++- .../dataTrendBar.vue | 55 +++-- .../dataTrendBarCombustible.vue | 55 +++-- .../dataTrendBarFactoryBurden.vue | 55 +++-- .../dataTrendBarFuel.vue | 55 +++-- .../dataTrendBarPro.vue | 55 +++-- .../dataTrendBarProcAuxMat.vue | 55 +++-- .../dataTrendBarProcess.vue | 55 +++-- .../dataTrendBarProcessingFuel.vue | 55 +++-- .../dataTrendBarProcessingLabor.vue | 55 +++-- .../dataTrendBarProduct.vue | 55 +++-- .../dataTrendBarSingleCombustible.vue | 55 +++-- .../dataTrendBarSingleFuel.vue | 55 +++-- .../operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 27 +- .../operatingSingleBar.vue | 66 +++-- .../operatingTopBar.vue | 58 +++-- .../productionOperatingLineBarSale.vue | 30 ++- .../totalOverview.vue | 18 +- .../verticalBarChart.vue | 44 +++- .../rawSheetYieldComponents/dataTrend.vue | 20 +- .../rawSheetYieldComponents/dataTrendBar.vue | 55 +++-- .../monthlyRelatedMetrics.vue | 20 +- .../rawSheetYieldComponents/operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 28 ++- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 20 +- .../operatingSingleBar.vue | 70 ++++-- .../operatingTopBar.vue | 58 +++-- .../verticalBarChart.vue | 80 +++--- .../yearRelatedMetrics.vue | 20 +- .../dataTrendBar.vue | 75 ++++-- .../dataTrendBarDouble.vue | 59 +++-- .../dataTrendBarProduct.vue | 59 +++-- .../monthlyOverview.vue | 18 +- .../monthlyRelatedMetrics.vue | 20 +- .../monthlyThreeRelatedMetrics.vue | 22 +- .../operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 27 +- .../operatingLineBarSaleSingle.vue | 6 +- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 20 +- .../operatingSingleBar.vue | 70 ++++-- .../operatingTopBar.vue | 58 +++-- .../totalOverview.vue | 18 +- .../verticalBarChart.vue | 44 +++- .../yearRelatedMetrics.vue | 25 +- .../yearThreeRelatedMetrics.vue | 27 +- .../totalProfitComponents/dataTrendBar.vue | 55 +++-- .../totalProfitComponents/monthlyOverview.vue | 6 +- .../totalProfitComponents/operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 28 ++- .../operatingLineBarSaleSingle.vue | 6 +- .../operatingSingleBar.vue | 67 +++-- .../totalProfitComponents/operatingTopBar.vue | 58 +++-- .../totalProfitComponents/totalOverview.vue | 6 +- .../verticalBarChart.vue | 44 +++- .../financeCosts.vue | 18 +- .../monthlyOverview.vue | 18 +- .../monthlyRelatedMetrics.vue | 22 +- .../operatingBar.vue | 27 +- .../operatingLineBarSale.vue | 26 +- .../operatingLineBarSaleSingle.vue | 6 +- .../operatingLineChart.vue | 20 +- .../operatingLineChartCumulative.vue | 20 +- .../operatingSingleBar.vue | 70 ++++-- .../operatingTopBar.vue | 58 +++-- .../proDataTrendBar.vue | 27 +- .../totalOverview.vue | 18 +- .../verticalBarChart.vue | 44 +++- .../yearRelatedMetrics.vue | 32 +-- 164 files changed, 4179 insertions(+), 2297 deletions(-) create mode 100644 src/assets/icons/svg/closeSider.svg create mode 100644 src/assets/icons/svg/openSider.svg diff --git a/src/assets/icons/svg/closeSider.svg b/src/assets/icons/svg/closeSider.svg new file mode 100644 index 00000000..83c5543d --- /dev/null +++ b/src/assets/icons/svg/closeSider.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/svg/openSider.svg b/src/assets/icons/svg/openSider.svg new file mode 100644 index 00000000..9703a46e --- /dev/null +++ b/src/assets/icons/svg/openSider.svg @@ -0,0 +1,12 @@ + + + 展开备份 + + + + + + + + + \ No newline at end of file diff --git a/src/views/home/components/Header.vue b/src/views/home/components/Header.vue index 76fd4f5e..36809742 100644 --- a/src/views/home/components/Header.vue +++ b/src/views/home/components/Header.vue @@ -6,16 +6,12 @@
{{ topTitle }}
-
- - -
-
+ + + + @@ -24,16 +20,15 @@
-
月份选择
+ value-format="timestamp" + style="width: 132px;height: 29px;" + @change="emitTimeRange" + />
@@ -45,19 +40,19 @@ export default { name: 'Header', props: { isFullScreen: { type: Boolean, default: false }, + openSider: { type: Boolean, default: false }, topTitle: { type: String, default: '' }, dateData: { type: Object, - default: {} // 默认值设为350px + default: () => ({}) }, }, data() { return { currentTime: '', timeTimer: null, - date: undefined, - // activeIndex: -1, - activeTime: 1, // 0=日,1=月,2=年(默认选中“日”) + date: Date.now(), // 使用时间戳格式 + activeTime: 1, // 0=日,1=月,2=年(默认选中"月") pageRoutes: [ { text: '营业收入', path: '/operatingRevenue/operatingRevenueIndex' }, { text: '利润分析', path: '/profitAnalysis' }, @@ -65,132 +60,109 @@ export default { { text: '成本分析', path: '/cost/cost' }, { text: '驾驶舱报表', path: '/cockpit' } ], - // 定义时间类型配置:text=按钮文字,pickerType=选择器类型,placeholder=占位符 - // timeTypes: [ - // { text: '日', pickerType: 'date', placeholder: '选择日期' }, - // { text: '月', pickerType: 'month', placeholder: '选择月份' }, - // { text: '年', pickerType: 'year', placeholder: '选择年份' } - // ] } }, - computed: { - // // 动态获取日期选择器类型 - // getPickerType() { - // return this.timeTypes[this.activeTime].pickerType; - // }, - // // 动态获取日期选择器占位符 - // getPickerPlaceholder() { - // return this.timeTypes[this.activeTime].placeholder; - // } - }, methods: { goToPage(path, index) { - // 1. 跳转到对应路由 this.$router.push(path); - // 2. 更新activeIndex,让当前点击项高亮 - this.activeIndex = index; }, + changeHomeSider() { this.$emit('siderOpenChange') }, changeFullScreen() { this.$emit('screenfullChange') }, padZero(num) { return num < 10 ? '0' + num : num }, + /** - * 核心方法1:根据维度计算时间区间(首次进入时基于赋值的当月日期,计算“当月第一天0点→次月第一天0点”) - * @returns {Object} 包含 start(开始时间)、end(结束时间)、dimension(维度)的区间对象 - */ + * 计算时间区间 + * @returns {Object} 包含 startTime、endTime、mode、targetMonth 的区间对象 + */ calculateTimeRange() { // 固定为月维度 const mode = 2; // 初始化时间戳为0(兜底值) let startTime = 0; let endTime = 0; - // 存储目标月份(仅数字,如10、12) + // 存储目标月份 let targetMonth = ''; - // 默认当前月份 - const defaultMoment = moment(); try { - // 仅处理月份维度:固定使用YYYY-MM格式解析日期 + // 使用 this.date(时间戳)创建 moment 对象 let targetMoment = this.date - ? moment(this.date, 'YYYY-MM') // 解析传入的月份(如"2025-10") - : defaultMoment; + ? moment(this.date) // 解析时间戳 + : moment(); // 如果 date 无效,使用当前时间 - // 验证日期是否有效,无效则使用当前月份兜底 + // 验证日期是否有效 if (!targetMoment.isValid()) { - console.warn('无效的月份格式(请使用YYYY-MM),已使用当前月份:', this.date); - targetMoment = defaultMoment; + console.warn('无效的日期,已使用当前月份:', this.date); + targetMoment = moment(); } - // 仅获取月份(数字格式,如10、12;若要两位格式如01、10,使用'MM') - targetMonth = targetMoment.format('M'); // 'M'是1-12,'MM'是01-12,可按需选择 + // 获取月份(数字格式,1-12) + targetMonth = targetMoment.format('M'); + console.log('当前选择的月份:', targetMonth); - // 打印仅含月份的结果 - console.log('targetMonth', targetMonth); + // 计算当月第一天00:00:00的时间戳 + startTime = targetMoment.startOf('month').valueOf(); + // 计算当月最后一天23:59:59的时间戳 + endTime = targetMoment.endOf('month').valueOf(); - // 计算当月第一天00:00:00(去掉毫秒)的毫秒级时间戳 - startTime = targetMoment.startOf('month').millisecond(0).valueOf(); - // 计算当月最后一天23:59:59(去掉毫秒)的毫秒级时间戳 - endTime = targetMoment.endOf('month').millisecond(0).valueOf(); - - // 【可选】调试输出:查看时间范围详情 - // console.log('月份时间范围:', { - // startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'), - // endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'), - // startTimeStamp: startTime, - // endTimeStamp: endTime - // }); + // 调试输出 + console.log('月份时间范围计算结果:', { + startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'), + endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'), + startTimeStamp: startTime, + endTimeStamp: endTime, + targetMonth: targetMonth + }); } catch (error) { console.error('计算月份时间范围时出错:', error); } - // 返回月份相关的所有信息:时间戳、维度、仅月份值 return { startTime, endTime, mode, - targetMonth // 现在仅为月份数字,如"10" + targetMonth }; }, + /** - * 核心方法2:传递时间区间给父组件(首次进入时触发,传递“当月第一天0点→次月第一天0点”) + * 传递时间区间给父组件 */ emitTimeRange() { const timeRange = this.calculateTimeRange(); this.$emit('timeRangeChange', timeRange); - // 调试用:查看首次传递的区间(如{start: "2025-10-01T00:00:00", end: "2025-11-01T00:00:00", dimension: "月"}) - console.log('当前时间区间:', timeRange); + console.log('触发时间范围变化:', timeRange); } }, watch: { - // 维度切换时:清空选择的日期,并传递当前维度的默认区间 + // 维度切换时:清空选择的日期 activeTime(newVal, oldVal) { if (newVal !== oldVal) { - this.date = undefined; - // this.emitTimeRange(); + this.date = Date.now(); // 重置为当前时间戳 + this.emitTimeRange(); } }, dateData: { immediate: true, // 初始化时立即执行 handler(newVal) { + console.log('dateData 变化:', newVal); if (newVal && (newVal.startTime || newVal.endTime)) { - // 优先使用 startTime 转换为月份格式 + // 优先使用 startTime const timeStamp = newVal.startTime || newVal.endTime; - if (timeStamp !== 0) { - const monthStr = moment(timeStamp).format('YYYY-MM'); - this.date = monthStr; // 赋值给选择器 + if (timeStamp && timeStamp !== 0) { + console.log('设置日期选择器时间为:', timeStamp); + this.date = timeStamp; // 直接使用时间戳 } } } } }, mounted() { - // 核心逻辑:首次进入页面,计算当月默认日期并赋值给选择器,同时传递区间 - const now = new Date(); - const year = now.getFullYear(); - const month = this.padZero(now.getMonth() + 1); // 月份从0开始,+1后补零(如1月→01) - // 赋值当月默认日期(格式:YYYY-MM,适配month类型选择器) - this.date = `${year}-${month}`; - // 确保选择器渲染完成后,传递“当月第一天0点→次月第一天0点”的区间 - this.$nextTick(() => this.emitTimeRange()); - }, + // 初始化默认日期为当前月份 + console.log('初始化日期选择器,当前时间戳:', this.date); + this.$nextTick(() => { + this.emitTimeRange(); + }); + } } @@ -211,7 +183,6 @@ export default { background-position: 0 0; box-sizing: border-box; position: relative; - /* 确保timeType绝对定位生效 */ .left-content { margin-top: 11px; @@ -278,12 +249,10 @@ export default { position: absolute; display: flex; align-items: center; - /* 垂直居中,避免元素高低错位 */ top: 42px; - right:10px; + right: 10px; margin-top: 18px; gap: 0; - /* 清除间隙,让按钮与选择器紧密连接 */ } .timeType .item { @@ -300,7 +269,6 @@ export default { text-align: center; cursor: pointer; overflow: hidden; - /* 选中按钮与未选中按钮倾斜角度统一,避免切换时跳动 */ } .timeType .item .item-text { @@ -313,13 +281,11 @@ export default { background: rgba(11, 88, 255, 1); color: rgba(249, 252, 255, 1); transform: skew(-20deg) !important; - /* 统一倾斜角度,修复原30deg的错位 */ box-shadow: 0 2px 8px rgba(11, 88, 255, 0.3); } .timeType .item.no-skew .item-text { transform: skew(20deg) !important; - /* 同步统一文字倾斜角度 */ } .dateP { @@ -335,7 +301,6 @@ export default { height: 28px; background: rgba(236, 244, 254, 1); transform: skew(-25deg); - /* 与按钮倾斜角度统一(原30deg改为25deg,避免视觉错位) */ font-family: PingFangSC, PingFang SC; font-weight: 400; font-size: 14px; @@ -345,19 +310,17 @@ export default { overflow: hidden; } - /* 补充:label文字抵消倾斜(原代码遗漏,导致文字倾斜) */ .dateP .label-text { display: inline-block; transform: skew(25deg); - /* 与label倾斜角度相反,确保文字正立 */ } .right-content { display: flex; - flex-direction: column; - margin-top: 12px; + margin-bottom: 60px; margin-right: 16px; - gap: 20px; + justify-content: flex-end; + gap: 10px; } .current-time { @@ -371,7 +334,6 @@ export default { .screen-btn { width: 26px; - margin-left: 300px; color: #00fff0; font-size: 26px; padding: 0; @@ -387,20 +349,16 @@ export default { position: relative; margin: 0 !important; - /* 1. 调整输入框文字:确保行高与输入框高度一致,垂直居中 */ .el-input__inner { height: 28px !important; width: 165px !important; text-align: center; padding-left: 15px !important; padding-right: 32px !important; - /* 给图标留空间,避免文字被遮挡 */ font-size: 14px !important; line-height: 28px !important; - /* 行高=输入框高度,文字垂直居中 */ color: rgba(237, 245, 253, 1) !important; vertical-align: middle !important; - /* 强制文字垂直对齐 */ clip-path: polygon(18px 0, 100% 0, 100% 100%, 0 100%); border: none !important; box-shadow: none !important; @@ -408,38 +366,27 @@ export default { border-left: 1px solid rgba(255, 255, 255, 0.2); } - /* 2. 调整图标容器:让图标与文字在同一水平线上 */ .el-input__prefix { left: auto !important; right: 8px !important; top: 50% !important; - /* 从40%改为50%,基于输入框垂直居中 */ transform: translateY(-50%) !important; - /* 向上偏移自身50%,精准居中 */ display: inline-flex !important; - /* 让容器内图标垂直居中 */ align-items: center !important; - /* 图标在容器内垂直居中 */ height: 28px !important; - /* 容器高度=输入框高度,避免偏移 */ } - /* 3. 调整图标本身:确保图标大小和对齐方式 */ .el-input__icon { color: #ffffff !important; font-size: 16px !important; line-height: 28px !important; - /* 图标行高=输入框高度,与文字对齐 */ vertical-align: middle !important; - /* 强制图标垂直对齐 */ } - /* 4. 图标伪类:确保颜色和对齐继承 */ .el-icon-date::before { color: #ffffff !important; font-size: 16px !important; line-height: inherit !important; - /* 继承父级行高,避免错位 */ } } diff --git a/src/views/home/components/coresBar.vue b/src/views/home/components/coresBar.vue index 6fed2846..fd957e67 100644 --- a/src/views/home/components/coresBar.vue +++ b/src/views/home/components/coresBar.vue @@ -97,21 +97,11 @@ export default { overflow: 'break', formatter: (value) => { const dateParts = value.split('-'); // ["2025", "07", "01"] - if (dateParts.length < 2) return value; // 处理异常格式 + if (dateParts.length < 2) return value; - switch (this.dateData.mode) { - case 1: // 日模式,显示“月-日” - // 确保有日的部分 - return dateParts.length >= 3 - ? `${dateParts[1]}月${dateParts[2]}日` - : `${dateParts[1]}月`; // fallback - case 2: // 月模式,显示“月” - return `${dateParts[1]}月`; - case 3: // 年模式,显示“年” - return `${dateParts[1]}月`; - default: // 默认月模式 - return `${dateParts[1]}月`; - } + // 去掉月份前面的0,然后加上"月" + const month = dateParts[1].replace(/^0+/, ''); + return `${month}月`; } }, data: this.xAxisData diff --git a/src/views/home/components/noRouterHeader.vue b/src/views/home/components/noRouterHeader.vue index 77f9b4ca..a7539327 100644 --- a/src/views/home/components/noRouterHeader.vue +++ b/src/views/home/components/noRouterHeader.vue @@ -18,17 +18,12 @@
-
月份选择
- +
@@ -49,76 +44,65 @@ export default { }, dateData: { type: Object, - default: {} // 默认值设为350px + default: () => ({}) }, }, data() { return { currentTime: '', timeTimer: null, - date: undefined, // 存储选择的日期(字符串格式:yyyy-MM-dd/yyyy-MM/yyyy) + date: Date.now(), // 使用当前时间戳作为初始值 activeTime: 1, // 默认月维度(0=日,1=月,2=年) - // timeTypes: [ - // { text: '日', pickerType: 'date', placeholder: '选择日期' }, - // { text: '月', pickerType: 'month', placeholder: '选择月份' }, - // { text: '年', pickerType: 'year', placeholder: '选择年份' } - // ] } }, - computed: { - // getPickerType() { - // return this.timeTypes[this.activeTime].pickerType; - // }, - // getPickerPlaceholder() { - // return this.timeTypes[this.activeTime].placeholder; - // } - }, watch: { activeTime(newVal, oldVal) { if (newVal !== oldVal) { - this.date = undefined; - // this.emitTimeRange(); + this.date = Date.now(); + this.emitTimeRange(); } }, dateData: { immediate: true, // 初始化时立即执行 handler(newVal) { - console.log('newVal', newVal); - + console.log('dateData 变化:', newVal); if (newVal && (newVal.startTime || newVal.endTime)) { - // 优先使用 startTime 转换为月份格式 + // 优先使用 startTime,如果没有则使用 endTime const timeStamp = newVal.startTime || newVal.endTime; - if (timeStamp !== 0) { - const monthStr = moment(timeStamp).format('YYYY-MM'); - this.date = monthStr; // 赋值给选择器 + if (timeStamp && timeStamp !== 0) { + console.log('设置日期选择器时间为:', timeStamp); + this.date = timeStamp; // 直接使用时间戳 } } } } }, mounted() { - // 初始化默认日期(当前月,格式:yyyy-MM) - // console.log(this.$router); - this.date = moment().format('YYYY-MM'); - this.$nextTick(() => this.emitTimeRange()); + // 初始化默认日期为当前月份 + console.log('初始化日期选择器,当前时间戳:', this.date); + this.$nextTick(() => { + this.emitTimeRange(); + }); + }, + beforeDestroy() { + // 清理定时器 + if (this.timeTimer) { + clearInterval(this.timeTimer); + } }, methods: { changeFullScreen() { this.$emit('screenfullChange'); }, handleReturn() { - console.log(this.$router); - - this.$router.go(-1); - }, - exportPDF() { - this.$emit('exportPDF'); + console.log('返回上一页'); + if (this.$router) { + this.$router.go(-1); + } }, /** - * 核心方法:用moment计算时间范围(时间戳格式) - * 日:选择日00:00:00 → 次日00:00:00 - * 月:当月1日00:00:00 → 次月1日00:00:00 - * 年:当年1月1日00:00:00 → 次年1月1日00:00:00 + * 计算时间范围(时间戳格式) + * 固定为月维度:当月1日00:00:00 → 当月最后一天23:59:59 */ calculateTimeRange() { // 固定为月维度 @@ -128,99 +112,53 @@ export default { let endTime = 0; // 存储目标月份(仅数字,如10、12) let targetMonth = ''; - // 默认当前月份 - const defaultMoment = moment(); try { - // 仅处理月份维度:固定使用YYYY-MM格式解析日期 + // 使用 this.date(时间戳)创建 moment 对象 let targetMoment = this.date - ? moment(this.date, 'YYYY-MM') // 解析传入的月份(如"2025-10") - : defaultMoment; + ? moment(this.date) + : moment(); // 如果 date 无效,使用当前时间 - // 验证日期是否有效,无效则使用当前月份兜底 + // 验证日期是否有效 if (!targetMoment.isValid()) { - console.warn('无效的月份格式(请使用YYYY-MM),已使用当前月份:', this.date); - targetMoment = defaultMoment; + console.warn('无效的日期,已使用当前月份:', this.date); + targetMoment = moment(); } - // 仅获取月份(数字格式,如10、12;若要两位格式如01、10,使用'MM') - targetMonth = targetMoment.format('M'); // 'M'是1-12,'MM'是01-12,可按需选择 + // 获取月份(数字格式,1-12) + targetMonth = targetMoment.format('M'); + console.log('当前选择的月份:', targetMonth); - // 打印仅含月份的结果 - console.log('targetMonth', targetMonth); + // 计算当月第一天00:00:00的时间戳 + startTime = targetMoment.startOf('month').valueOf(); + // 计算当月最后一天23:59:59的时间戳 + endTime = targetMoment.endOf('month').valueOf(); - // 计算当月第一天00:00:00(去掉毫秒)的毫秒级时间戳 - startTime = targetMoment.startOf('month').millisecond(0).valueOf(); - // 计算当月最后一天23:59:59(去掉毫秒)的毫秒级时间戳 - endTime = targetMoment.endOf('month').millisecond(0).valueOf(); - - // 【可选】调试输出:查看时间范围详情 - // console.log('月份时间范围:', { - // startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'), - // endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'), - // startTimeStamp: startTime, - // endTimeStamp: endTime - // }); + // 调试输出 + console.log('月份时间范围计算结果:', { + startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'), + endTime: moment(endTime).format('YYYY-MM-DD HH:mm:ss'), + startTimeStamp: startTime, + endTimeStamp: endTime, + targetMonth: targetMonth + }); } catch (error) { console.error('计算月份时间范围时出错:', error); } - // 返回月份相关的所有信息:时间戳、维度、仅月份值 + // 返回月份相关的所有信息:时间戳、维度、月份值 return { startTime, endTime, mode, - targetMonth // 现在仅为月份数字,如"10" + targetMonth }; }, - // calculateTimeRange() { - // let startTime = 0; - // let endTime = 0; - // const mode = 2; // 1=日,2=月,3=年 - // const defaultMoment = moment(); // 默认当前时间 - - // const targetMoment = this.date - // ? moment(this.date, this.getPickerType === 'date' ? 'YYYY-MM-DD' : (this.getPickerType === 'month' ? 'YYYY-MM' : 'YYYY')) - // : defaultMoment; - - // // if (!targetMoment.isValid()) { - // // console.error('无效日期:', this.date); - // // return { startTime, endTime, mode }; - // // } - - // // // 1. 日维度:00:00:00 → 23:59:59(无毫秒) - // // if (this.activeTime === 0) { - // // startTime = targetMoment.startOf('day').millisecond(0).valueOf(); - // // endTime = targetMoment.endOf('day').millisecond(0).valueOf(); - // // } - - // // 2. 月维度:当月1日00:00:00 → 当月最后一天23:59:59(无毫秒) - // // else if (this.activeTime === 1) { - // startTime = targetMoment.startOf('month').millisecond(0).valueOf(); - // endTime = targetMoment.endOf('month').millisecond(0).valueOf(); - // // } - - // // // 3. 年维度:当年1月1日00:00:00 → 当年最后一天23:59:59(无毫秒) - // // else if (this.activeTime === 2) { - // // startTime = targetMoment.startOf('year').millisecond(0).valueOf(); - // // endTime = targetMoment.endOf('year').millisecond(0).valueOf(); - // // } - - // // // 调试输出:验证是否去掉毫秒 - // // console.log('时间范围计算结果:', { - // // mode, - // // startTime: moment(startTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 00:00:00 - // // endTime: moment(endTime * 1000).format('YYYY-MM-DD HH:mm:ss'), // 格式:2025-11-30 23:59:59(无毫秒) - // // startTimeStamp: startTime, // 秒级时间戳(如:1764422400) - // // endTimeStamp: endTime // 秒级时间戳(如:1764508799) - // // }); - - // return { startTime, endTime, mode }; - // }, // 传递时间范围给父组件 emitTimeRange() { const timeRange = this.calculateTimeRange(); + console.log('触发时间范围变化:', timeRange); this.$emit('timeRangeChange', timeRange); } } @@ -258,7 +196,6 @@ export default { /* 左侧标题区域 */ .left-content { margin-top: 11px; - // margin-left: 350px; height: 55px; display: flex; align-items: center; @@ -347,48 +284,36 @@ export default { } /* 右侧全屏按钮区域 */ - .right-content { - display: flex; - // flex-direction: column; - margin-top: 12px; - margin-right: 10px; - gap: 21px; - } + .right-content { + display: flex; + margin-top: 12px; + margin-right: 10px; + gap: 21px; + } - // .current-time { - // color: #FFFFFF; - // font-family: PingFangSC, PingFang SC; - // font-weight: 500; - // font-size: 22px; - // line-height: 24px; - // letter-spacing: 1px; - // } + .screen-btn { + width: 26px; + height: 26px; + color: #00fff0; + font-size: 26px; + padding: 0; + } - .screen-btn { - width: 26px; - height: 26px; - color: #00fff0; - font-size: 26px; - padding: 0; - } + .home-btn { + width: 26px; + height: 26px; + color: #00fff0; + font-size: 26px; + padding: 0; + } - .home-btn { - width: 26px; - height: 26px; - // margin-left: 300px; - color: #00fff0; - font-size: 26px; - padding: 0; - } - - .return-btn { - width: 26px; - height: 26px; - // margin-left: 300px; - color: #00fff0; - font-size: 26px; - padding: 0; - } + .return-btn { + width: 26px; + height: 26px; + color: #00fff0; + font-size: 26px; + padding: 0; + } } /* 日期选择器自定义样式 */ diff --git a/src/views/home/components/productBar.vue b/src/views/home/components/productBar.vue index 7b0ab154..27034d67 100644 --- a/src/views/home/components/productBar.vue +++ b/src/views/home/components/productBar.vue @@ -115,27 +115,18 @@ export default { color: 'rgba(0, 0, 0, 0.45)', fontSize: 12, interval: 0, - // 这里可以根据需要调整标签的显示方式 + // width: 38, + overflow: 'break', formatter: (value) => { const dateParts = value.split('-'); // ["2025", "07", "01"] - if (dateParts.length < 2) return value; // 处理异常格式 + if (dateParts.length < 2) return value; - switch (this.dateData.mode) { - case 1: // 日模式,显示“月-日” - // 确保有日的部分 - return dateParts.length >= 3 - ? `${dateParts[1]}月${dateParts[2]}日` - : `${dateParts[1]}月`; // fallback - case 2: // 月模式,显示“月” - return `${dateParts[1]}月`; - case 3: // 年模式,显示“年” - return `${dateParts[0]}年`; - default: // 默认月模式 - return `${dateParts[1]}月`; - } + // 去掉月份前面的0,然后加上"月" + const month = dateParts[1].replace(/^0+/, ''); + return `${month}月`; } }, - data: xAxisData // 使用提取出的 X 轴数据 + data: xAxisData } ], yAxis: { diff --git a/src/views/home/components/purchase-Item.vue b/src/views/home/components/purchase-Item.vue index 0edb8c5f..f76f8373 100644 --- a/src/views/home/components/purchase-Item.vue +++ b/src/views/home/components/purchase-Item.vue @@ -25,7 +25,7 @@
-
@@ -42,7 +42,7 @@ 'percent-exceed': item.currentValue >= item.targetValue, 'percent-below': item.currentValue < item.targetValue }"> - {{ item.progress }}% + {{ item.progressDisplay }}
@@ -88,29 +88,101 @@ export default { } }, methods: { + // 解析rate字符串,提取百分比数值 + parseRateString(rateStr) { + if (!rateStr || typeof rateStr !== 'string') { + return { displayText: '0%', progressValue: 0 }; + } + + // 尝试匹配百分比数字,如"减亏93%"中的93 + const match = rateStr.match(/(\d+(\.\d+)?)%/); + if (match) { + const percentValue = parseFloat(match[1]); + return { + displayText: rateStr, // 保持原字符串显示 + progressValue: percentValue // 提取的百分比数值用于进度条 + }; + } + + // 如果没有匹配到百分比,尝试解析纯数字 + const numMatch = rateStr.match(/\d+(\.\d+)?/); + if (numMatch) { + const numValue = parseFloat(numMatch[0]); + return { + displayText: rateStr, + progressValue: numValue + }; + } + + // 默认返回 + return { + displayText: '0%', + progressValue: 0 + }; + }, + transformData(rawData) { // 定义指标映射关系,包括名称、对应的数据键和路由 const Mapping = [ - { key: 'operatingRevenue', name: '营业收入·万元', route: '/operatingRevenue/operatingRevenueIndex' }, - { key: 'operatingIncome', name: '经营性利润·万元', route: '/operatingProfit/operatingProfit' }, - { key: 'totalProfit', name: '利润总额·万元', route: '/totalProfit/totalProfit' }, - { key: 'grossMargin', name: '毛利率·%', route: '/grossMargin/grossMargin' } + { + key: 'operatingRevenue', + name: '营业收入·万元', + route: '/operatingRevenue/operatingRevenueIndex', + isPercentage: true // 需要加%符号 + }, + { + key: 'operatingIncome', + name: '经营性利润·万元', + route: '/operatingProfit/operatingProfit', + isPercentage: false // 不需要加%符号,使用原始rate字符串 + }, + { + key: 'totalProfit', + name: '利润总额·万元', + route: '/totalProfit/totalProfit', + isPercentage: false // 不需要加%符号,使用原始rate字符串 + }, + { + key: 'grossMargin', + name: '毛利率·%', + route: '/grossMargin/grossMargin', + isPercentage: true // 需要加%符号 + } ]; // 遍历映射关系,转换数据 return Mapping.map(mappingItem => { // 关键修复3:兜底更严谨,避免rawData[mappingItem.key]不存在导致报错 - const data = rawData[mappingItem.key] || { rate: 0, real: 0, target: 0 }; + const data = rawData[mappingItem.key] || { rate: '0%', real: 0, target: 0 }; // 额外兜底:避免data中的属性为undefined const target = data.target || 0; const real = data.real || 0; - const rate = data.rate || 0; + const rate = data.rate || '0%'; + + // 解析rate字符串 + const parsedRate = this.parseRateString(rate); + + // 进度条宽度:限制在0-100之间 + const progressWidth = Math.min(Math.max(parsedRate.progressValue, 0), 100); + + // 显示文本处理 + let progressDisplay; + if (mappingItem.isPercentage) { + // 对于需要加%的指标,确保有%符号 + progressDisplay = parsedRate.displayText.includes('%') + ? parsedRate.displayText + : `${parsedRate.displayText}%`; + } else { + // 对于经营性利润和利润总额,直接使用原始rate字符串 + progressDisplay = parsedRate.displayText; + } return { name: mappingItem.name, targetValue: target, currentValue: real, - progress: Math.round(rate), // 将小数率转换为百分比并四舍五入 + progressWidth: progressWidth, // 用于进度条宽度 + progressDisplay: progressDisplay, // 用于显示文本 route: mappingItem.route }; }); diff --git a/src/views/home/expenseAnalysisComponents/dataTrend.vue b/src/views/home/expenseAnalysisComponents/dataTrend.vue index 4e85ed58..7d16937d 100644 --- a/src/views/home/expenseAnalysisComponents/dataTrend.vue +++ b/src/views/home/expenseAnalysisComponents/dataTrend.vue @@ -98,11 +98,11 @@ export default { const diff = Number(item.diff) || 0; // 计算达标标识(≥100 → 1,<100 → 0) - const flag = this.getRateFlag(rate); + const flag = this.getRateFlag(rate, real, budget); // 填充数组 months.push(month); - rates.push(Math.round(rate * 100)); // 转为百分比并取整 + rates.push(rate); // 转为百分比并取整 reals.push(real); budgets.push(budget); diffs.push(diff); @@ -127,10 +127,18 @@ export default { * @param {Number} rate - 完成率(原始值,如1.2 → 120%) * @returns {Number} 1: 达标(≥100%),0: 未达标(<100%) */ - getRateFlag(rate) { + getRateFlag(rate, real, target) { + // 先处理无效值的情况 if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→0) - }, + + // 实际值和目标值都为0时,算作达标 + if (real === 0 && target === 0 && rate === 0) { + return 1; // 达标 + } + + // 其他情况:rate >= 100 或 rate === 0 时达标 + return (rate >= 100) ? 1 : 0; + } }, }; diff --git a/src/views/home/expenseAnalysisComponents/dataTrendBar.vue b/src/views/home/expenseAnalysisComponents/dataTrendBar.vue index d1ea3496..345a991a 100644 --- a/src/views/home/expenseAnalysisComponents/dataTrendBar.vue +++ b/src/views/home/expenseAnalysisComponents/dataTrendBar.vue @@ -155,8 +155,20 @@ export default { // 关键:去掉换行,让文字在一行显示,适配小尺寸 formatter: function (params) { const diff = data.diffs || []; + const flags = data.flags || []; const currentDiff = diff[params.dataIndex] || 0; - return `{rate|${currentDiff}}{text|差值}`; + const currentFlag = flags[params.dataIndex] || 0; + + const prefix = currentFlag === 1 ? '+' : '-'; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, backgroundColor: { type: 'linear', @@ -194,11 +206,20 @@ export default { fontSize: 11, // 缩小字体,适配小尺寸 lineHeight: 20 // 垂直居中 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/expenseAnalysisComponents/monthlyRelatedMetrics.vue b/src/views/home/expenseAnalysisComponents/monthlyRelatedMetrics.vue index 5811b038..d94f3192 100644 --- a/src/views/home/expenseAnalysisComponents/monthlyRelatedMetrics.vue +++ b/src/views/home/expenseAnalysisComponents/monthlyRelatedMetrics.vue @@ -84,10 +84,18 @@ export default { }, methods: { // 达标标识判断(≥100返回1,<100返回0) - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→0) - }, +getRateFlag(rate, real, target) { + if (isNaN(rate) || rate === null || rate === undefined) return 0; + + // 1. 完成率 >= 100 => 达标 + if (rate >= 100) return 1; + + // 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标 + if (rate === 0 && target === 0) return 1; + + // 其他情况 => 未达标 + return 0; +}, // 处理费用数据 updateCostData(data) { @@ -104,17 +112,16 @@ export default { // 整合flag字段 this.manageCostData = { ...manageItem, - flag: this.getRateFlag(manageItem.rate) + flag: this.getRateFlag(manageItem.rate, manageItem.real, manageItem.budget) }; this.saleCostData = { ...saleItem, - flag: this.getRateFlag(saleItem.rate) + flag: this.getRateFlag(saleItem.rate, saleItem.real, saleItem.budget) }; this.financeCostData = { ...financeItem, - flag: this.getRateFlag(financeItem.rate) + flag: this.getRateFlag(financeItem.rate, financeItem.real, financeItem.budget) }; - // 调试日志 console.log('管理费用数据:', this.manageCostData); console.log('销售费用数据:', this.saleCostData); diff --git a/src/views/home/expenseAnalysisComponents/operatingBar.vue b/src/views/home/expenseAnalysisComponents/operatingBar.vue index 411b1c12..c95f3e7e 100644 --- a/src/views/home/expenseAnalysisComponents/operatingBar.vue +++ b/src/views/home/expenseAnalysisComponents/operatingBar.vue @@ -181,8 +181,20 @@ export default { height: 20, formatter: (params) => { const diff = data.diff || []; + const flags = data.flags || []; const currentDiff = diff[params.dataIndex] || 0; - return `{rate|${currentDiff}}{text|差值}`; + const currentFlag = flags[params.dataIndex] || 0; + + const prefix = currentFlag === 1 ? '+' : '-'; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, backgroundColor: { type: 'linear', @@ -210,11 +222,20 @@ export default { fontSize: 11, lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/expenseAnalysisComponents/operatingLineBarSale.vue b/src/views/home/expenseAnalysisComponents/operatingLineBarSale.vue index 0a666379..d67621c1 100644 --- a/src/views/home/expenseAnalysisComponents/operatingLineBarSale.vue +++ b/src/views/home/expenseAnalysisComponents/operatingLineBarSale.vue @@ -65,21 +65,31 @@ export default { // 只创建一次图表实例 this.myChart = echarts.init(chartDom); + this.myChart.getZr().on('click', (params) => { + console.log('params', params); - // 绑定点击事件(只绑定一次,永久生效) - this.myChart.on('click', (params) => { // 提取点击的基地名称 - const itemName = params.name; + // const itemName = params.name; + let itemName = undefined // 根据映射表获取对应的序号(未匹配到则返回0或其他默认值) + let pointInPixel = [params.offsetX, params.offsetY]; + if (this.myChart.containPixel('grid', pointInPixel)) { + let pointInGrid = this.myChart.convertFromPixel({ + seriesIndex: 0 + }, pointInPixel); + let xIndex = pointInGrid[0]; //索引 + let handleIndex = Number(xIndex); + let seriesObj = this.myChart.getOption(); //图表object对象 + var op = this.myChart.getOption(); + //获得图表中点击的列 + itemName = op.xAxis[0].data[handleIndex]; //获取点击的列名 + console.log(itemName, 'monthmonthmonth'); + console.log(handleIndex, seriesObj); + }; const baseIndex = this.baseNameToIndexMap[itemName] || 0; - // 兼容不同图表类型的value:柱状图value是数值,折线图是[横坐标, 纵坐标] - // const itemValue = Array.isArray(params.value) ? params.value[1] : params.value; - // const seriesName = params.seriesName; - console.log(`你点击了【${itemName}】(序号:${baseIndex})`); - // 路由跳转时携带序号(或名称+序号) this.$router.push({ path: 'expenseAnalysisBase', query: { // 使用query传递参数(推荐),也可使用params diff --git a/src/views/home/expenseAnalysisComponents/operatingLineChart.vue b/src/views/home/expenseAnalysisComponents/operatingLineChart.vue index b42360bc..c80cf8dd 100644 --- a/src/views/home/expenseAnalysisComponents/operatingLineChart.vue +++ b/src/views/home/expenseAnalysisComponents/operatingLineChart.vue @@ -88,10 +88,18 @@ export default { * @param {number} rate 处理后的rate值(已*100) * @returns {0|1} flag值 */ - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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; +}, /** * 核心处理函数:在所有数据都准备好后,才组装 chartData */ @@ -105,7 +113,7 @@ export default { const groupReal = [this.groupData.real]; // 实际值数组 const groupRate = [this.groupData.rate]; // 完成率数组 // 新增:集团rate对应的flag - const groupFlag = [this.getRateFlag(groupRate[0])]; + const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; console.log('集团数据数组:', { groupTarget, @@ -126,7 +134,7 @@ export default { const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0); // 新增:每个工厂rate对应的flag数组 - const factoryFlags = factoryRate.map(rate => this.getRateFlag(rate)); + const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); // 3. 组装最终的chartData(供子组件使用) this.chartData = { diff --git a/src/views/home/expenseAnalysisComponents/operatingLineChartCumulative.vue b/src/views/home/expenseAnalysisComponents/operatingLineChartCumulative.vue index e4b25f86..8d030131 100644 --- a/src/views/home/expenseAnalysisComponents/operatingLineChartCumulative.vue +++ b/src/views/home/expenseAnalysisComponents/operatingLineChartCumulative.vue @@ -83,9 +83,17 @@ export default { * @param {number} rate 处理后的rate值(已*100) * @returns {0|1} flag值 */ - getRateFlag(rate) { + getRateFlag(rate, real, target) { + // 先处理无效值的情况 if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→0) + console.log(rate, real, target) + + // 实际值和目标值都为0时,算作达标 + if (real === 0 && target === 0 && rate === 0) { + return 1; // 达标 + } + // 其他情况:rate >= 100 或 rate === 0 时达标 + return (rate >= 100) ? 1 : 0; }, /** * 核心处理函数:在所有数据都准备好后,才组装 chartData @@ -100,8 +108,7 @@ export default { const groupReal = [this.groupData.real]; // 实际值数组 const groupRate = [this.groupData.rate]; // 完成率数组 // 新增:集团rate对应的flag - const groupFlag = [this.getRateFlag(groupRate[0])]; - + const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; console.log('集团数据数组:', { groupTarget, groupDiff, @@ -121,7 +128,8 @@ export default { const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0); // 新增:每个工厂rate对应的flag数组 - const factoryFlags = factoryRate.map(rate => this.getRateFlag(rate)); + const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); + console.log('factoryFlags', factoryFlags); // 3. 组装最终的chartData(供子组件使用) this.chartData = { diff --git a/src/views/home/expenseAnalysisComponents/operatingSingleBar.vue b/src/views/home/expenseAnalysisComponents/operatingSingleBar.vue index d1562ceb..c05b8cfa 100644 --- a/src/views/home/expenseAnalysisComponents/operatingSingleBar.vue +++ b/src/views/home/expenseAnalysisComponents/operatingSingleBar.vue @@ -90,8 +90,16 @@ export default { } }, 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, + borderWidth: 0 }, }, { @@ -105,52 +113,61 @@ export default { width: 68, height: 20, // 关键:去掉换行,让文字在一行显示,适配小尺寸 - formatter: function (params) { - // 假设 params.data 是完成率数值(如 139) - // // 2. 模板字符串拼接富文本标签 + 动态值 - return `{rate|${diff}}{text|差值}`; + formatter: (params) => { + + // const flags = flags || []; + const currentDiff = diff || 0; + const currentFlag = this.detailData?.flag || 0; + const prefix = currentFlag === 1 ? '+' : '-'; + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, backgroundColor: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + 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: 0, color: 'rgba(205, 215, 224, 0.6)' }, + { 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], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/expenseAnalysisComponents/operatingTopBar.vue b/src/views/home/expenseAnalysisComponents/operatingTopBar.vue index d8afea7c..64e0bd7b 100644 --- a/src/views/home/expenseAnalysisComponents/operatingTopBar.vue +++ b/src/views/home/expenseAnalysisComponents/operatingTopBar.vue @@ -160,51 +160,60 @@ export default { height: 20, // 关键:去掉换行,让文字在一行显示,适配小尺寸 formatter: (params) => { - // const { rate = 0, diff = 0 } = params.data || {}; - return `{rate|${diff}}{text|差值}`; + + // const flags = flags || []; + const currentDiff = diff || 0; + const currentFlag = data.flags[0] || 0; + const prefix = currentFlag === 1 ? '+' : '-'; + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, - // 核心样式:匹配CSS需求 backgroundColor: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + 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: 0, color: 'rgba(205, 215, 224, 0.6)' }, + { 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], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/expenseAnalysisComponents/verticalBarChart.vue b/src/views/home/expenseAnalysisComponents/verticalBarChart.vue index 0ed4bba3..6268b78f 100644 --- a/src/views/home/expenseAnalysisComponents/verticalBarChart.vue +++ b/src/views/home/expenseAnalysisComponents/verticalBarChart.vue @@ -43,10 +43,18 @@ export default { } }, methods: { - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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() { const chartDom = this.$refs[this.refName]; if (!chartDom) { @@ -61,7 +69,7 @@ export default { this.myChart = echarts.init(chartDom); const diff = this.detailData.diff || 0 const rate = this.detailData.rate || 0 - const flagValue = this.getRateFlag(this.detailData.rate) || 0 + const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0 const option = { tooltip: { @@ -150,54 +158,61 @@ export default { width: 68, height: 20, // 关键:去掉换行,让文字在一行显示,适配小尺寸 - formatter: function (params) { - // 假设 params.data 是完成率数值(如 139) - // // 2. 模板字符串拼接富文本标签 + 动态值 - return `{rate|${diff}}{text|差值}`; + formatter: (params) => { + + // const flags = flags || []; + const currentDiff = diff || 0; + const currentFlag = flagValue || 0; + const prefix = currentFlag === 1 ? '+' : '-'; + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, - // formatter: '', - // 核心样式:匹配CSS需求 backgroundColor: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + 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: 0, color: 'rgba(205, 215, 224, 0.6)' }, + { 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], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/expenseAnalysisComponents/yearRelatedMetrics.vue b/src/views/home/expenseAnalysisComponents/yearRelatedMetrics.vue index 39c061e3..495e871a 100644 --- a/src/views/home/expenseAnalysisComponents/yearRelatedMetrics.vue +++ b/src/views/home/expenseAnalysisComponents/yearRelatedMetrics.vue @@ -84,10 +84,18 @@ export default { }, methods: { // 达标标识判断(≥100返回1,<100返回0) - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→0) - }, +getRateFlag(rate, real, target) { + if (isNaN(rate) || rate === null || rate === undefined) return 0; + + // 1. 完成率 >= 100 => 达标 + if (rate >= 100) return 1; + + // 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标 + if (rate === 0 && target === 0) return 1; + + // 其他情况 => 未达标 + return 0; +}, // 处理费用数据 updateCostData(data) { @@ -104,15 +112,15 @@ export default { // 整合flag字段 this.manageCostData = { ...manageItem, - flag: this.getRateFlag(manageItem.rate) + flag: this.getRateFlag(manageItem.rate, manageItem.real, manageItem.budget) }; this.saleCostData = { ...saleItem, - flag: this.getRateFlag(saleItem.rate) + flag: this.getRateFlag(saleItem.rate, saleItem.real, saleItem.budget) }; this.financeCostData = { ...financeItem, - flag: this.getRateFlag(financeItem.rate) + flag: this.getRateFlag(financeItem.rate, financeItem.real, financeItem.budget) }; // 调试日志 diff --git a/src/views/home/fullCostAnalysisComponents/dataTrendBar.vue b/src/views/home/fullCostAnalysisComponents/dataTrendBar.vue index 741489bd..f8db212e 100644 --- a/src/views/home/fullCostAnalysisComponents/dataTrendBar.vue +++ b/src/views/home/fullCostAnalysisComponents/dataTrendBar.vue @@ -104,7 +104,7 @@ export default { diffs.push(monthData.diff || 0); // 生成达标状态(复用 getRateFlag 逻辑) - flags.push(this.getRateFlag(completeRate)); + flags.push(this.getRateFlag(completeRate, monthData.real, monthData.target)); }); return { @@ -122,7 +122,9 @@ export default { // 重构 chartD:替换硬编码数据为动态解析数据 chartD() { // 获取动态解析的趋势数据 - const { months, rates, reals, targets, flags,diffs } = this.trendParsedData; + const { months, rates, reals, targets, flags, diffs } = this.trendParsedData; + console.log('flags',flags); + // 销量场景数据(保留原有结构,替换数据来源) const salesData = { allPlaceNames: months, // 优先用基地名称,无则用月份 @@ -189,8 +191,21 @@ export default { // 关键:去掉换行,让文字在一行显示,适配小尺寸 formatter: function (params) { const diff = diffs || []; + // const flags = flags || []; const currentDiff = diff[params.dataIndex] || 0; - return `{rate|${currentDiff}}{text|差值}`; + const currentFlag = flags[params.dataIndex] || 0; + console.log('currentFlag', flags, params.dataIndex); + + const prefix = currentFlag === 1 ? '+' : '-'; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, backgroundColor: { type: 'linear', @@ -220,19 +235,27 @@ export default { lineHeight: 20, rich: { text: { - // 缩小宽度和内边距,适配68px容器 - width: 'auto', // 自动宽度,替代固定40px - padding: [5, 10, 5, 0], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } @@ -277,10 +300,18 @@ export default { this.isDropdownShow = false; }, // 复用达标状态判断方法 - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return (rate >= 100 || rate === 0) ? 1 : 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; +}, } }; diff --git a/src/views/home/fullCostAnalysisComponents/monthlyOverview.vue b/src/views/home/fullCostAnalysisComponents/monthlyOverview.vue index bb4ab2c4..3a411dc2 100644 --- a/src/views/home/fullCostAnalysisComponents/monthlyOverview.vue +++ b/src/views/home/fullCostAnalysisComponents/monthlyOverview.vue @@ -87,7 +87,7 @@ export default { target: 0, thb: 0, ...rawData, - flag: this.getRateFlag(rawData.completeRate) // 新增flag字段 + flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段 }; } }, @@ -106,10 +106,18 @@ export default { * @param {number} rate 完成率(原始值,如89代表89%) * @returns {0|1} flag值 */ - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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; +}, } } diff --git a/src/views/home/fullCostAnalysisComponents/operatingBar.vue b/src/views/home/fullCostAnalysisComponents/operatingBar.vue index 95a7ae43..35cdc7a3 100644 --- a/src/views/home/fullCostAnalysisComponents/operatingBar.vue +++ b/src/views/home/fullCostAnalysisComponents/operatingBar.vue @@ -181,8 +181,20 @@ export default { height: 20, formatter: (params) => { const diff = data.diff || []; + const flags = data.flags || []; const currentDiff = diff[params.dataIndex] || 0; - return `{rate|${currentDiff}}{text|差值}`; + const currentFlag = flags[params.dataIndex] || 0; + + const prefix = currentFlag === 1 ? '+' : '-'; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, backgroundColor: { type: 'linear', @@ -210,11 +222,20 @@ export default { fontSize: 11, lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/fullCostAnalysisComponents/operatingLineBarSale.vue b/src/views/home/fullCostAnalysisComponents/operatingLineBarSale.vue index 2becd693..b3a076ed 100644 --- a/src/views/home/fullCostAnalysisComponents/operatingLineBarSale.vue +++ b/src/views/home/fullCostAnalysisComponents/operatingLineBarSale.vue @@ -67,15 +67,28 @@ export default { this.myChart = echarts.init(chartDom); // 绑定点击事件(只绑定一次,永久生效) - this.myChart.on('click', (params) => { - // 提取点击的基地名称 - const itemName = params.name; - // 根据映射表获取对应的序号(未匹配到则返回0或其他默认值) - const baseIndex = this.baseNameToIndexMap[itemName] || 0; + this.myChart.getZr().on('click', (params) => { + console.log('params', params); - // 兼容不同图表类型的value:柱状图value是数值,折线图是[横坐标, 纵坐标] - // const itemValue = Array.isArray(params.value) ? params.value[1] : params.value; - // const seriesName = params.seriesName; + // 提取点击的基地名称 + // const itemName = params.name; + let itemName = undefined + // 根据映射表获取对应的序号(未匹配到则返回0或其他默认值) + let pointInPixel = [params.offsetX, params.offsetY]; + if (this.myChart.containPixel('grid', pointInPixel)) { + let pointInGrid = this.myChart.convertFromPixel({ + seriesIndex: 0 + }, pointInPixel); + let xIndex = pointInGrid[0]; //索引 + let handleIndex = Number(xIndex); + let seriesObj = this.myChart.getOption(); //图表object对象 + var op = this.myChart.getOption(); + //获得图表中点击的列 + itemName = op.xAxis[0].data[handleIndex]; //获取点击的列名 + console.log(itemName,'monthmonthmonth'); + console.log(handleIndex, seriesObj); + }; + const baseIndex = this.baseNameToIndexMap[itemName] || 0; console.log(`你点击了【${itemName}】(序号:${baseIndex})`); diff --git a/src/views/home/fullCostAnalysisComponents/operatingLineChart.vue b/src/views/home/fullCostAnalysisComponents/operatingLineChart.vue index d22bc816..805db584 100644 --- a/src/views/home/fullCostAnalysisComponents/operatingLineChart.vue +++ b/src/views/home/fullCostAnalysisComponents/operatingLineChart.vue @@ -82,10 +82,18 @@ export default { * @param {number} rate 完成率(原始值,如80代表80%) * @returns {0|1} flag值 */ - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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; +}, /** * 核心处理函数:解析thisMonData,组装集团和工厂数据 @@ -105,7 +113,7 @@ export default { diff: [ksxnData.diff], reals: [ksxnData.real], rate: [ksxnData.completeRate], - flags: [this.getRateFlag(ksxnData.completeRate)], + flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)], thb: [ksxnData.thb] // 新增thb字段(如果子组件需要) }; @@ -120,7 +128,7 @@ export default { diff: factoryDataList.map(item => item.diff || 0), // 差值 reals: factoryDataList.map(item => item.real || 0), // 实际值 rates: factoryDataList.map(item => item.completeRate || 0), // 完成率 - flags: factoryDataList.map(item => this.getRateFlag(item.completeRate)), // 完成率标识 + flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识 thb: factoryDataList.map(item => item.thb || 0) // thb字段 }; diff --git a/src/views/home/fullCostAnalysisComponents/operatingLineChartCumulative.vue b/src/views/home/fullCostAnalysisComponents/operatingLineChartCumulative.vue index 7e874244..e89de029 100644 --- a/src/views/home/fullCostAnalysisComponents/operatingLineChartCumulative.vue +++ b/src/views/home/fullCostAnalysisComponents/operatingLineChartCumulative.vue @@ -82,10 +82,18 @@ export default { * @param {number} rate 完成率(原始值,如80代表80%) * @returns {0|1} flag值 */ - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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; +}, /** * 核心处理函数:解析thisMonData,组装集团和工厂数据 @@ -105,7 +113,7 @@ export default { diff: [ksxnData.diff], reals: [ksxnData.real], rate: [ksxnData.completeRate], - flags: [this.getRateFlag(ksxnData.completeRate)], + flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)], thb: [ksxnData.thb] // 新增thb字段(如果子组件需要) }; @@ -120,7 +128,7 @@ export default { diff: factoryDataList.map(item => item.diff || 0), // 差值 reals: factoryDataList.map(item => item.real || 0), // 实际值 rates: factoryDataList.map(item => item.completeRate || 0), // 完成率 - flags: factoryDataList.map(item => this.getRateFlag(item.completeRate)), // 完成率标识 + flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识 thb: factoryDataList.map(item => item.thb || 0) // thb字段 }; diff --git a/src/views/home/fullCostAnalysisComponents/operatingSingleBar.vue b/src/views/home/fullCostAnalysisComponents/operatingSingleBar.vue index 7c522ceb..18cd1325 100644 --- a/src/views/home/fullCostAnalysisComponents/operatingSingleBar.vue +++ b/src/views/home/fullCostAnalysisComponents/operatingSingleBar.vue @@ -25,7 +25,7 @@ export default { // const bgImageUrl = require('@/assets/img/labelBg.png'); const rate = this.detailData?.completeRate || 0 const diff = this.detailData?.diff || 0 - console.log('diff', diff); + console.log('diff', this.detailData); const seriesData = [ { @@ -90,8 +90,16 @@ export default { } }, 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, + borderWidth: 0 }, }, { @@ -105,52 +113,61 @@ export default { width: 68, height: 20, // 关键:去掉换行,让文字在一行显示,适配小尺寸 - formatter: function (params) { - // 假设 params.data 是完成率数值(如 139) - // // 2. 模板字符串拼接富文本标签 + 动态值 - return `{rate|${diff}}{text|差值}`; + formatter: (params) => { + + // const flags = flags || []; + const currentDiff = diff || 0; + const currentFlag = this.detailData?.flag || 0; + const prefix = currentFlag === 1 ? '+' : ''; + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, backgroundColor: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + 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: 0, color: 'rgba(205, 215, 224, 0.6)' }, + { 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], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/fullCostAnalysisComponents/operatingTopBar.vue b/src/views/home/fullCostAnalysisComponents/operatingTopBar.vue index a8e3e21e..63cc4879 100644 --- a/src/views/home/fullCostAnalysisComponents/operatingTopBar.vue +++ b/src/views/home/fullCostAnalysisComponents/operatingTopBar.vue @@ -160,51 +160,60 @@ export default { height: 20, // 关键:去掉换行,让文字在一行显示,适配小尺寸 formatter: (params) => { - // const { rate = 0, diff = 0 } = params.data || {}; - return `{rate|${diff}}{text|差值}`; + + // const flags = flags || []; + const currentDiff = diff || 0; + const currentFlag = flags[0] || 0; + const prefix = currentFlag === 1 ? '+' : ''; + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, - // 核心样式:匹配CSS需求 backgroundColor: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + 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: 0, color: 'rgba(205, 215, 224, 0.6)' }, + { 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], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/fullCostAnalysisComponents/relatedIndicatorsAnalysis.vue b/src/views/home/fullCostAnalysisComponents/relatedIndicatorsAnalysis.vue index ffe1cda0..4a44c8d2 100644 --- a/src/views/home/fullCostAnalysisComponents/relatedIndicatorsAnalysis.vue +++ b/src/views/home/fullCostAnalysisComponents/relatedIndicatorsAnalysis.vue @@ -14,7 +14,7 @@
@@ -25,7 +25,7 @@
@@ -36,7 +36,7 @@
@@ -47,7 +47,7 @@
@@ -58,7 +58,7 @@
@@ -150,10 +150,18 @@ export default { path: path }) }, - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 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; +}, handleChange(value) { console.log('value', value, this.relatedData); if (value === 'month') { diff --git a/src/views/home/fullCostAnalysisComponents/totalOverview.vue b/src/views/home/fullCostAnalysisComponents/totalOverview.vue index c0593162..3f911cdf 100644 --- a/src/views/home/fullCostAnalysisComponents/totalOverview.vue +++ b/src/views/home/fullCostAnalysisComponents/totalOverview.vue @@ -90,7 +90,7 @@ export default { target: 0, thb: 0, ...rawData, - flag: this.getRateFlag(rawData.completeRate) // 新增flag字段 + flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段 }; } }, @@ -109,10 +109,18 @@ export default { * @param {number} rate 完成率(原始值,如89代表89%) * @returns {0|1} flag值 */ - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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; +}, } } diff --git a/src/views/home/fullCostAnalysisComponents/verticalBarChart.vue b/src/views/home/fullCostAnalysisComponents/verticalBarChart.vue index 79ee50ca..4b766b32 100644 --- a/src/views/home/fullCostAnalysisComponents/verticalBarChart.vue +++ b/src/views/home/fullCostAnalysisComponents/verticalBarChart.vue @@ -56,6 +56,8 @@ export default { } // 解构数据,避免重复取值 + console.log('this.detailData', this.detailData); + const { diff, completeRate, real, target, flag } = this.detailData; // 确保数值为数字类型 const realValue = Number(real) || 0; @@ -110,7 +112,21 @@ export default { offset: [0, 25], width: 68, height: 20, - formatter: `{rate|${diffValue}}{text|差值}`, + formatter: (params) => { + const currentDiff = diffValue || 0; + const currentFlag = flagValue || 0; + + const prefix = currentFlag === 1 ? '+' : ''; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } + }, backgroundColor: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, @@ -125,11 +141,35 @@ export default { 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 }, - rate: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', color: '#30B590', fontSize: 11, lineHeight: 20 } + 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 + } } }, // 修复:flag颜色判断独立绑定到实际值柱子 diff --git a/src/views/home/grossMarginComponents/dataTrend.vue b/src/views/home/grossMarginComponents/dataTrend.vue index d98c8f73..d5252997 100644 --- a/src/views/home/grossMarginComponents/dataTrend.vue +++ b/src/views/home/grossMarginComponents/dataTrend.vue @@ -98,11 +98,11 @@ export default { const diff = Number(item.diff) || 0; // 计算达标标识(≥100 → 1,<100 → 0) - const flag = this.getRateFlag(rate); + const flag = this.getRateFlag(rate, real, budget); // 填充数组 months.push(month); - rates.push(Math.round(rate * 100)); // 转为百分比并取整 + rates.push(rate); // 转为百分比并取整 reals.push(real); budgets.push(budget); diffs.push(diff); @@ -127,12 +127,18 @@ export default { * @param {Number} rate - 完成率(原始值,如1.2 → 120%) * @returns {Number} 1: 达标(≥100%),0: 未达标(<100%) */ - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - // 原始rate若为小数(如0.8 → 80%,1.1 → 110%),则*100后判断 - const ratePercent = rate; - return ratePercent >= 100 ? 1 : 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; +}, }, }; diff --git a/src/views/home/grossMarginComponents/dataTrendBar.vue b/src/views/home/grossMarginComponents/dataTrendBar.vue index 563588bd..c348fd48 100644 --- a/src/views/home/grossMarginComponents/dataTrendBar.vue +++ b/src/views/home/grossMarginComponents/dataTrendBar.vue @@ -152,52 +152,63 @@ export default { width: 68, height: 20, // 关键:去掉换行,让文字在一行显示,适配小尺寸 - formatter: function (params) { + formatter: (params) => { const diff = data.diffs || []; + const flags = data.flags || []; const currentDiff = diff[params.dataIndex] || 0; - return `{rate|${currentDiff}}{text|差值}`; + const currentFlag = flags[params.dataIndex] || 0; + + const prefix = currentFlag === 1 ? '+' : '-'; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, backgroundColor: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + 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: 0, color: 'rgba(205, 215, 224, 0.6)' }, + { 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], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } @@ -234,88 +245,6 @@ export default { ] }; - // 毛利率场景数据 - const grossProfitData = { - series: [ - // 1. 完成率(折线图) - { - name: '完成率', - type: 'line', - yAxisIndex: 1, - lineStyle: { color: 'rgba(40, 138, 255, .5)', width: 2 }, - itemStyle: { - color: 'rgba(40, 138, 255, 1)', - borderColor: 'rgba(40, 138, 255, 1)', - borderWidth: 2, - radius: 4 - }, - areaStyle: { - opacity: 0.2, - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { offset: 0, color: 'rgba(40, 138, 255, .9)' }, - { offset: 1, color: 'rgba(40, 138, 255, 0)' } - ]) - }, - data: [106.7, 96.9, 106.5, 106.1, 93.8, 105.9], // 毛利率完成率(%) - symbol: 'circle', - symbolSize: 6 - }, - // 2. 目标(柱状图) - { - name: '预算', - type: 'bar', - yAxisIndex: 0, - 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: [30, 32, 31, 33, 32, 34] // 目标毛利率(万元) - }, - // 3. 实际(柱状图) - { - name: '实际', - type: 'bar', - yAxisIndex: 0, - barWidth: 14, - itemStyle: { - color: (params) => { - const safeFlag = [1, 0, 1, 1, 0, 1]; // 达标状态 - 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: [32, 31, 33, 35, 30, 36] // 实际毛利率(万元) - } - ] - }; - // 根据按钮状态返回对应数据 return salesData; } diff --git a/src/views/home/grossMarginComponents/monthlyRelatedMetrics.vue b/src/views/home/grossMarginComponents/monthlyRelatedMetrics.vue index 564ca073..25e5e55d 100644 --- a/src/views/home/grossMarginComponents/monthlyRelatedMetrics.vue +++ b/src/views/home/grossMarginComponents/monthlyRelatedMetrics.vue @@ -84,10 +84,18 @@ export default { } }) }, - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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) { // 修复核心:正确获取默认值(调用 factory 函数) @@ -104,12 +112,12 @@ export default { // 整合flag字段 this.ytdIncomeData = { ...incomeItem, - flag: this.getRateFlag(incomeItem.rate) + flag: this.getRateFlag(incomeItem.rate, incomeItem.real, incomeItem.budget) }; this.ytdCostData = { ...costItem, - flag: this.getRateFlag(costItem.rate) + flag: this.getRateFlag(costItem.rate, costItem.real, costItem.budget) }; console.log('累计收入数据:', this.ytdIncomeData); diff --git a/src/views/home/grossMarginComponents/operatingBar.vue b/src/views/home/grossMarginComponents/operatingBar.vue index 411b1c12..c95f3e7e 100644 --- a/src/views/home/grossMarginComponents/operatingBar.vue +++ b/src/views/home/grossMarginComponents/operatingBar.vue @@ -181,8 +181,20 @@ export default { height: 20, formatter: (params) => { const diff = data.diff || []; + const flags = data.flags || []; const currentDiff = diff[params.dataIndex] || 0; - return `{rate|${currentDiff}}{text|差值}`; + const currentFlag = flags[params.dataIndex] || 0; + + const prefix = currentFlag === 1 ? '+' : '-'; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, backgroundColor: { type: 'linear', @@ -210,11 +222,20 @@ export default { fontSize: 11, lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/grossMarginComponents/operatingLineBarSale.vue b/src/views/home/grossMarginComponents/operatingLineBarSale.vue index 36e9a441..9ac02be5 100644 --- a/src/views/home/grossMarginComponents/operatingLineBarSale.vue +++ b/src/views/home/grossMarginComponents/operatingLineBarSale.vue @@ -67,15 +67,39 @@ export default { this.myChart = echarts.init(chartDom); // 绑定点击事件(只绑定一次,永久生效) - this.myChart.on('click', (params) => { - // 提取点击的基地名称 - const itemName = params.name; - // 根据映射表获取对应的序号(未匹配到则返回0或其他默认值) - const baseIndex = this.baseNameToIndexMap[itemName] || 0; + // this.myChart.on('click', (params) => { + // // 提取点击的基地名称 + // const itemName = params.name; + // // 根据映射表获取对应的序号(未匹配到则返回0或其他默认值) + // const baseIndex = this.baseNameToIndexMap[itemName] || 0; - // 兼容不同图表类型的value:柱状图value是数值,折线图是[横坐标, 纵坐标] - // const itemValue = Array.isArray(params.value) ? params.value[1] : params.value; - // const seriesName = params.seriesName; + // // 兼容不同图表类型的value:柱状图value是数值,折线图是[横坐标, 纵坐标] + // // const itemValue = Array.isArray(params.value) ? params.value[1] : params.value; + // // const seriesName = params.seriesName; + + // console.log(`你点击了【${itemName}】(序号:${baseIndex})`); + this.myChart.getZr().on('click', (params) => { + console.log('params', params); + + // 提取点击的基地名称 + // const itemName = params.name; + let itemName = undefined + // 根据映射表获取对应的序号(未匹配到则返回0或其他默认值) + let pointInPixel = [params.offsetX, params.offsetY]; + if (this.myChart.containPixel('grid', pointInPixel)) { + let pointInGrid = this.myChart.convertFromPixel({ + seriesIndex: 0 + }, pointInPixel); + let xIndex = pointInGrid[0]; //索引 + let handleIndex = Number(xIndex); + let seriesObj = this.myChart.getOption(); //图表object对象 + var op = this.myChart.getOption(); + //获得图表中点击的列 + itemName = op.xAxis[0].data[handleIndex]; //获取点击的列名 + console.log(itemName, 'monthmonthmonth'); + console.log(handleIndex, seriesObj); + }; + const baseIndex = this.baseNameToIndexMap[itemName] || 0; console.log(`你点击了【${itemName}】(序号:${baseIndex})`); diff --git a/src/views/home/grossMarginComponents/operatingLineChart.vue b/src/views/home/grossMarginComponents/operatingLineChart.vue index 547a3ead..9a5f8e0e 100644 --- a/src/views/home/grossMarginComponents/operatingLineChart.vue +++ b/src/views/home/grossMarginComponents/operatingLineChart.vue @@ -89,10 +89,18 @@ export default { * @param {number} rate 处理后的rate值(已*100) * @returns {0|1} flag值 */ - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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; +}, /** * 核心处理函数:在所有数据都准备好后,才组装 chartData @@ -107,7 +115,7 @@ export default { const groupReal = [this.groupData.real]; // 实际值数组 const groupRate = [this.groupData.rate]; // 完成率数组 // 新增:集团rate对应的flag - const groupFlag = [this.getRateFlag(groupRate[0])]; + const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; console.log('集团数据数组:', { groupTarget, @@ -128,7 +136,7 @@ export default { const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0); // 新增:每个工厂rate对应的flag数组 - const factoryFlags = factoryRate.map(rate => this.getRateFlag(rate)); + const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget)); // 3. 组装最终的chartData(供子组件使用) this.chartData = { diff --git a/src/views/home/grossMarginComponents/operatingLineChartCumulative.vue b/src/views/home/grossMarginComponents/operatingLineChartCumulative.vue index 6089d7bb..83ec00ee 100644 --- a/src/views/home/grossMarginComponents/operatingLineChartCumulative.vue +++ b/src/views/home/grossMarginComponents/operatingLineChartCumulative.vue @@ -84,10 +84,18 @@ export default { * @returns {0|1} flag值 */ - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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; +}, /** * 核心处理函数:在所有数据都准备好后,才组装 chartData @@ -102,7 +110,7 @@ export default { const groupReal = [this.groupData.real]; // 实际值数组 const groupRate = [this.groupData.rate]; // 完成率数组 // 新增:集团rate对应的flag - const groupFlag = [this.getRateFlag(groupRate[0])]; + const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])]; console.log('集团数据数组:', { groupTarget, @@ -123,7 +131,7 @@ export default { const factoryRate = this.factoryData.map(item => item.rate || 0); const factoryDiff = this.factoryData.map(item => item.diff || 0); // 新增:每个工厂rate对应的flag数组 - const factoryFlags = factoryRate.map(rate => this.getRateFlag(rate)); + const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.rate, item.budget)); // 3. 组装最终的chartData(供子组件使用) this.chartData = { diff --git a/src/views/home/grossMarginComponents/operatingSingleBar.vue b/src/views/home/grossMarginComponents/operatingSingleBar.vue index d1562ceb..997f61a4 100644 --- a/src/views/home/grossMarginComponents/operatingSingleBar.vue +++ b/src/views/home/grossMarginComponents/operatingSingleBar.vue @@ -90,8 +90,16 @@ export default { } }, 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, + borderWidth: 0 }, }, { @@ -105,52 +113,64 @@ export default { width: 68, height: 20, // 关键:去掉换行,让文字在一行显示,适配小尺寸 - formatter: function (params) { - // 假设 params.data 是完成率数值(如 139) - // // 2. 模板字符串拼接富文本标签 + 动态值 - return `{rate|${diff}}{text|差值}`; + formatter: (params) => { + + // const flags = flags || []; + const currentDiff = diff || 0; + const currentFlag = this.detailData?.flag || 0; + // console.log('flags[params.dataIndex]', flags); + + const prefix = currentFlag === 1 ? '+' : '-'; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, backgroundColor: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + 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: 0, color: 'rgba(205, 215, 224, 0.6)' }, + { 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], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/grossMarginComponents/operatingTopBar.vue b/src/views/home/grossMarginComponents/operatingTopBar.vue index 2ff0a37d..a67cae09 100644 --- a/src/views/home/grossMarginComponents/operatingTopBar.vue +++ b/src/views/home/grossMarginComponents/operatingTopBar.vue @@ -160,51 +160,63 @@ export default { height: 20, // 关键:去掉换行,让文字在一行显示,适配小尺寸 formatter: (params) => { - // const { rate = 0, diff = 0 } = params.data || {}; - return `{rate|${diff}}{text|差值}`; + + // const flags = flags || []; + const currentDiff = diff || 0; + const currentFlag = data.flags[0] || 0; + // console.log('flags[params.dataIndex]', flags); + + const prefix = currentFlag === 1 ? '+' : ''; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, - // 核心样式:匹配CSS需求 backgroundColor: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + 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: 0, color: 'rgba(205, 215, 224, 0.6)' }, + { 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], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/grossMarginComponents/verticalBarChart.vue b/src/views/home/grossMarginComponents/verticalBarChart.vue index 0ed4bba3..73ae2fc2 100644 --- a/src/views/home/grossMarginComponents/verticalBarChart.vue +++ b/src/views/home/grossMarginComponents/verticalBarChart.vue @@ -43,10 +43,18 @@ export default { } }, methods: { - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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() { const chartDom = this.$refs[this.refName]; if (!chartDom) { @@ -61,7 +69,7 @@ export default { this.myChart = echarts.init(chartDom); const diff = this.detailData.diff || 0 const rate = this.detailData.rate || 0 - const flagValue = this.getRateFlag(this.detailData.rate) || 0 + const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0 const option = { tooltip: { @@ -150,54 +158,64 @@ export default { width: 68, height: 20, // 关键:去掉换行,让文字在一行显示,适配小尺寸 - formatter: function (params) { - // 假设 params.data 是完成率数值(如 139) - // // 2. 模板字符串拼接富文本标签 + 动态值 - return `{rate|${diff}}{text|差值}`; + formatter: (params) => { + + // const flags = flags || []; + const currentDiff = diff || 0; + const currentFlag = flagValue || 0; + // console.log('flags[params.dataIndex]', flags); + + const prefix = currentFlag === 1 ? '+' : '-'; + + // 根据标志位选择不同的样式类 + if (currentFlag === 1) { + // 达标 - 使用 rate-achieved 样式 + return `{achieved|${prefix}${currentDiff}}{text|差值}`; + } else { + // 未达标 - 使用 rate-unachieved 样式 + return `{unachieved|${prefix}${currentDiff}}{text|差值}`; + } }, - // formatter: '', - // 核心样式:匹配CSS需求 backgroundColor: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + 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: 0, color: 'rgba(205, 215, 224, 0.6)' }, + { 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], // 缩小内边距 + width: 'auto', + padding: [5, 10, 5, 0], align: 'center', - color: '#464646', // 文字灰色 - fontSize: 11, // 缩小字体,适配小尺寸 - lineHeight: 20 // 垂直居中 + color: '#464646', + fontSize: 11, + lineHeight: 20 }, - rate: { + achieved: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', - color: '#30B590', + 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 } diff --git a/src/views/home/grossMarginComponents/yearRelatedMetrics.vue b/src/views/home/grossMarginComponents/yearRelatedMetrics.vue index 04eb67a7..88da76c7 100644 --- a/src/views/home/grossMarginComponents/yearRelatedMetrics.vue +++ b/src/views/home/grossMarginComponents/yearRelatedMetrics.vue @@ -90,10 +90,18 @@ export default { }) }, // 保留原flag判断逻辑(≥100返回1,<100返回0) - getRateFlag(rate) { - if (isNaN(rate) || rate === null || rate === undefined) return 0; - return +(rate >= 100 || rate === 0); // + 号将布尔值转为数字(true→1,false→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) { // 数据兜底:确保是数组且长度≥2 @@ -108,12 +116,12 @@ export default { // 整合flag字段到收入/全成本数据中 this.incomeData = { ...incomeItem, - flag: this.getRateFlag(incomeItem.rate) + flag: this.getRateFlag(incomeItem.rate, incomeItem.real, incomeItem.budget) }; this.totalCostData = { ...totalCostItem, - flag: this.getRateFlag(totalCostItem.rate) + flag: this.getRateFlag(totalCostItem.rate, totalCostItem.real, totalCostItem.budget) }; // 调试:确认数据赋值正确 diff --git a/src/views/home/index.vue b/src/views/home/index.vue index 87e7a219..2c21fd82 100644 --- a/src/views/home/index.vue +++ b/src/views/home/index.vue @@ -1,8 +1,9 @@