新增页面
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<header class="report-header">
|
||||
<!-- 左侧区域:logo + 标题 -->
|
||||
<div class="left-content">
|
||||
<img style="height: 36px;" src="../../../assets/img/cnbm.png" alt="benmaLogo" >
|
||||
<img style="height: 36px;" src="../../../assets/img/cnbm.png" alt="benmaLogo">
|
||||
<div class="top-title">{{ topTitle }}</div>
|
||||
</div>
|
||||
|
||||
@@ -24,16 +24,16 @@
|
||||
|
||||
<!-- 时间选择区域:日/月/年按钮 + label + 日期选择器 -->
|
||||
<div class="timeType">
|
||||
<div class="item" v-for="(item, index) in timeTypes" :key="index" @click="activeTime = index"
|
||||
<!-- <div class="item" v-for="(item, index) in timeTypes" :key="index" @click="activeTime = index"
|
||||
:class="{ 'no-skew': activeTime === index }">
|
||||
<span class="item-text">{{ item.text }}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="dateP">
|
||||
<div class="label">
|
||||
<span class="label-text">日期选择</span>
|
||||
<span class="label-text">月份选择</span>
|
||||
</div>
|
||||
<el-date-picker v-model="date" :type="getPickerType" :placeholder="getPickerPlaceholder"
|
||||
class="custom-date-picker" style="width: 132px;height: 29px;" @change="emitTimeRange" />
|
||||
<el-date-picker v-model="date" type="month" placeholder="请选择月份" class="custom-date-picker"
|
||||
style="width: 132px;height: 29px;" @change="emitTimeRange" />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -52,32 +52,32 @@ export default {
|
||||
currentTime: '',
|
||||
timeTimer: null,
|
||||
date: undefined,
|
||||
activeIndex: -1,
|
||||
// activeIndex: -1,
|
||||
activeTime: 1, // 0=日,1=月,2=年(默认选中“日”)
|
||||
pageRoutes: [
|
||||
{ text: '营业收入', path: '/operatingRevenue' },
|
||||
{ text: '营业收入', path: '/operatingRevenue/operatingRevenueIndex' },
|
||||
{ text: '利润分析', path: '/profitAnalysis' },
|
||||
{ text: '产销率库存分析', path: '/PSIAnal' },
|
||||
{ text: '成本分析', path: '/cost/cost' },
|
||||
{ text: '驾驶舱报表', path: '/cockpit' }
|
||||
],
|
||||
// 定义时间类型配置:text=按钮文字,pickerType=选择器类型,placeholder=占位符
|
||||
timeTypes: [
|
||||
{ text: '日', pickerType: 'date', placeholder: '选择日期' },
|
||||
{ text: '月', pickerType: 'month', placeholder: '选择月份' },
|
||||
{ text: '年', pickerType: 'year', 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;
|
||||
}
|
||||
// // 动态获取日期选择器类型
|
||||
// getPickerType() {
|
||||
// return this.timeTypes[this.activeTime].pickerType;
|
||||
// },
|
||||
// // 动态获取日期选择器占位符
|
||||
// getPickerPlaceholder() {
|
||||
// return this.timeTypes[this.activeTime].placeholder;
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
goToPage(path, index) {
|
||||
@@ -93,48 +93,57 @@ export default {
|
||||
* @returns {Object} 包含 start(开始时间)、end(结束时间)、dimension(维度)的区间对象
|
||||
*/
|
||||
calculateTimeRange() {
|
||||
// 固定为月维度
|
||||
const mode = 2;
|
||||
// 初始化时间戳为0(兜底值)
|
||||
let startTime = 0;
|
||||
let endTime = 0;
|
||||
const mode = this.activeTime + 1; // 1=日,2=月,3=年
|
||||
const defaultMoment = moment(); // 默认当前时间
|
||||
// 存储目标月份(仅数字,如10、12)
|
||||
let targetMonth = '';
|
||||
// 默认当前月份
|
||||
const defaultMoment = moment();
|
||||
|
||||
const targetMoment = this.date
|
||||
? moment(this.date, this.getPickerType === 'date' ? 'YYYY-MM-DD' : (this.getPickerType === 'month' ? 'YYYY-MM' : 'YYYY'))
|
||||
: defaultMoment;
|
||||
try {
|
||||
// 仅处理月份维度:固定使用YYYY-MM格式解析日期
|
||||
let targetMoment = this.date
|
||||
? moment(this.date, 'YYYY-MM') // 解析传入的月份(如"2025-10")
|
||||
: defaultMoment;
|
||||
|
||||
if (!targetMoment.isValid()) {
|
||||
console.error('无效日期:', this.date);
|
||||
return { startTime, endTime, mode };
|
||||
}
|
||||
// 验证日期是否有效,无效则使用当前月份兜底
|
||||
if (!targetMoment.isValid()) {
|
||||
console.warn('无效的月份格式(请使用YYYY-MM),已使用当前月份:', this.date);
|
||||
targetMoment = defaultMoment;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
// 仅获取月份(数字格式,如10、12;若要两位格式如01、10,使用'MM')
|
||||
targetMonth = targetMoment.format('M'); // 'M'是1-12,'MM'是01-12,可按需选择
|
||||
|
||||
// 2. 月维度:当月1日00:00:00 → 当月最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 1) {
|
||||
// 打印仅含月份的结果
|
||||
console.log('targetMonth', targetMonth);
|
||||
|
||||
// 计算当月第一天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
|
||||
// });
|
||||
} catch (error) {
|
||||
console.error('计算月份时间范围时出错:', error);
|
||||
}
|
||||
|
||||
// 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('时间范围计算结果:', {
|
||||
// 返回月份相关的所有信息:时间戳、维度、仅月份值
|
||||
return {
|
||||
startTime,
|
||||
endTime,
|
||||
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 };
|
||||
targetMonth // 现在仅为月份数字,如"10"
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 核心方法2:传递时间区间给父组件(首次进入时触发,传递“当月第一天0点→次月第一天0点”)
|
||||
@@ -305,7 +314,7 @@ export default {
|
||||
}
|
||||
|
||||
.dateP .label {
|
||||
width: 70px;
|
||||
width: 165px;
|
||||
height: 28px;
|
||||
background: rgba(236, 244, 254, 1);
|
||||
transform: skew(-25deg);
|
||||
@@ -356,7 +365,7 @@ export default {
|
||||
::v-deep .custom-date-picker {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
width: 132px !important;
|
||||
width: 165px !important;
|
||||
height: 28px !important;
|
||||
position: relative;
|
||||
margin: 0 !important;
|
||||
@@ -364,7 +373,7 @@ export default {
|
||||
/* 1. 调整输入框文字:确保行高与输入框高度一致,垂直居中 */
|
||||
.el-input__inner {
|
||||
height: 28px !important;
|
||||
width: 132px !important;
|
||||
width: 165px !important;
|
||||
text-align: center;
|
||||
padding-left: 15px !important;
|
||||
padding-right: 32px !important;
|
||||
|
||||
145
src/views/home/components/changeBase.vue
Normal file
145
src/views/home/components/changeBase.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div class="changeBase">
|
||||
<div class="base-item" @click="handleClick(index)" v-for="(item, index) in buttonList" :key="item"
|
||||
:style="{ zIndex: activeButton === index ? 10 : 1 }">
|
||||
<img :src="activeButton === index ? imgMap.bgBase[item] : imgMap.base[item]" :alt="`${item}基地`" :title="item">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 导入基地图片(按统一命名规范)
|
||||
import baseYixing from '@/assets/images/base/宜兴.png';
|
||||
import baseZhangzhou from '@/assets/images/base/漳州.png';
|
||||
import baseZigong from '@/assets/images/base/自贡.png';
|
||||
import baseTongcheng from '@/assets/images/base/桐城.png';
|
||||
import baseLuoyang from '@/assets/images/base/洛阳.png';
|
||||
import baseHefei from '@/assets/images/base/合肥.png';
|
||||
import baseSuqian from '@/assets/images/base/宿迁.png';
|
||||
import baseQinhuangdao from '@/assets/images/base/秦皇岛.png';
|
||||
|
||||
// 导入选中态基地图片
|
||||
import bgBaseYixing from '@/assets/images/bgBase/宜兴.png';
|
||||
import bgBaseZhangzhou from '@/assets/images/bgBase/漳州.png';
|
||||
import bgBaseZigong from '@/assets/images/bgBase/自贡.png';
|
||||
import bgBaseTongcheng from '@/assets/images/bgBase/桐城.png';
|
||||
import bgBaseLuoyang from '@/assets/images/bgBase/洛阳.png';
|
||||
import bgBaseHefei from '@/assets/images/bgBase/合肥.png';
|
||||
import bgBaseSuqian from '@/assets/images/bgBase/宿迁.png';
|
||||
import bgBaseQinhuangdao from '@/assets/images/bgBase/秦皇岛.png';
|
||||
|
||||
export default {
|
||||
name: "BaseSelector",
|
||||
props: {
|
||||
factory: {
|
||||
type: Number,
|
||||
default: 1, // 默认选中宜兴(序号1)
|
||||
validator: (val) => val >= 1 && val <= 8 // 校验序号范围
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeButton: 0, // 初始化默认选中索引0(宜兴)
|
||||
buttonList: ['宜兴', '漳州', '自贡', '桐城', '洛阳', '合肥', '宿迁', '秦皇岛'],
|
||||
imgMap: {
|
||||
base: {
|
||||
宜兴: baseYixing,
|
||||
漳州: baseZhangzhou,
|
||||
自贡: baseZigong,
|
||||
桐城: baseTongcheng,
|
||||
洛阳: baseLuoyang,
|
||||
合肥: baseHefei,
|
||||
宿迁: baseSuqian,
|
||||
秦皇岛: baseQinhuangdao
|
||||
},
|
||||
bgBase: {
|
||||
宜兴: bgBaseYixing,
|
||||
漳州: bgBaseZhangzhou,
|
||||
自贡: bgBaseZigong,
|
||||
桐城: bgBaseTongcheng,
|
||||
洛阳: bgBaseLuoyang,
|
||||
合肥: bgBaseHefei,
|
||||
宿迁: bgBaseSuqian,
|
||||
秦皇岛: bgBaseQinhuangdao
|
||||
}
|
||||
},
|
||||
baseNameToIndex: {
|
||||
宜兴: 1,
|
||||
漳州: 2,
|
||||
自贡: 3,
|
||||
桐城: 4,
|
||||
洛阳: 5,
|
||||
合肥: 6,
|
||||
宿迁: 7,
|
||||
秦皇岛: 8
|
||||
}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听父组件传递的factory变化,同步本地选中索引
|
||||
factory: {
|
||||
handler(newVal) {
|
||||
// 强制转换为数字,避免非数字值导致错误
|
||||
const val = Number(newVal);
|
||||
if (val >= 1 && val <= 8) {
|
||||
this.activeButton = val - 1; // 序号1→索引0(宜兴)
|
||||
} else {
|
||||
this.activeButton = 0; // 非法值默认选中宜兴
|
||||
}
|
||||
console.log('当前选中基地:', this.buttonList[this.activeButton], '序号:', newVal);
|
||||
},
|
||||
immediate: true // 初始化立即执行
|
||||
},
|
||||
// 监听本地选中索引变化,向父组件发送事件
|
||||
activeButton(newVal) {
|
||||
const selectedIndex = newVal + 1; // 索引0→序号1
|
||||
this.$emit('baseChange', selectedIndex);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick(index) {
|
||||
this.activeButton = index;
|
||||
},
|
||||
getIndexByName(name) {
|
||||
return this.baseNameToIndex[name] || 1;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 修复:初始化时触发事件,传递默认选中的宜兴序号(1)
|
||||
this.$emit('baseChange', 1);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.changeBase {
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
align-items: center;
|
||||
|
||||
.base-item {
|
||||
width: 234px;
|
||||
height: 81px;
|
||||
font-family: YouSheBiaoTiHei;
|
||||
font-size: 38px;
|
||||
line-height: 54px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
margin-right: -35px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -8,6 +8,16 @@
|
||||
{{ name }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="isShowTab" class="tab-group">
|
||||
<!-- 月度Tab:点击切换状态,动态绑定样式 -->
|
||||
<div class="tab-item" :class="{ active: activeTab === 'month' }" @click="handleTabClick('month')">
|
||||
月度
|
||||
</div>
|
||||
<!-- 累计Tab:点击切换状态,动态绑定样式 -->
|
||||
<div class="tab-item" :class="{ active: activeTab === 'total' }" @click="handleTabClick('total')">
|
||||
累计
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cockpitContainer-body">
|
||||
<slot>
|
||||
@@ -22,12 +32,22 @@ export default {
|
||||
name: 'Container',
|
||||
components: {},
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
props: ['name', 'size', 'icon', 'topSize'],
|
||||
props: ['name', 'size', 'icon', 'topSize','isShowTab'],
|
||||
data() {
|
||||
return {};
|
||||
return {
|
||||
activeTab: 'month' // 初始化激活的Tab(支持父组件传默认值)
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
methods: {
|
||||
handleTabClick(tabType) {
|
||||
this.activeTab = tabType;
|
||||
// 向父组件派发Tab切换事件,传递当前选中的Tab类型
|
||||
this.$emit('tabChange', tabType);
|
||||
// 可选:同时传递更详细的信息(如标签名)
|
||||
// this.$emit('tabChange', { type: tabType, name: tabType === 'month' ? '月度' : '累计' });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -43,35 +63,37 @@ export default {
|
||||
|
||||
.content-top {
|
||||
height: 60px;
|
||||
.title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
/* 垂直居中关键属性 */
|
||||
height: 100%;
|
||||
/* 继承父容器高度,确保垂直居中范围 */
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
font-size: 30px;
|
||||
margin-right: 12px;
|
||||
margin-top: 4px;
|
||||
/* 图标和文字之间的间距 */
|
||||
flex-shrink: 0;
|
||||
/* 防止图标被压缩 */
|
||||
}
|
||||
.title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
/* 垂直居中关键属性 */
|
||||
height: 100%;
|
||||
/* 继承父容器高度,确保垂直居中范围 */
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
font-size: 30px;
|
||||
margin-right: 12px;
|
||||
margin-top: 4px;
|
||||
/* 图标和文字之间的间距 */
|
||||
flex-shrink: 0;
|
||||
/* 防止图标被压缩 */
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
color: #000000;
|
||||
letter-spacing: 3px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
// 移除固定行高,避免影响垂直对齐
|
||||
// line-height: 60px;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
color: #000000;
|
||||
letter-spacing: 3px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
// 移除固定行高,避免影响垂直对齐
|
||||
// line-height: 60px;
|
||||
}
|
||||
// width: 547px;
|
||||
// background: url(../../../assets/img/contentTopBasic.png) no-repeat;
|
||||
// background-size: 100% 100%;
|
||||
@@ -106,11 +128,12 @@ export default {
|
||||
background-size: 100% 100%;
|
||||
background-position: 0 0;
|
||||
}
|
||||
&__rawTopTitleLarge {
|
||||
background: url(../../../assets/img/rawTopTitleLarge.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
&__rawTopTitleLarge {
|
||||
background: url(../../../assets/img/rawTopTitleLarge.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -167,11 +190,19 @@ export default {
|
||||
background-size: 100% 100%;
|
||||
background-position: 0 0;
|
||||
}
|
||||
&__rawTopBg {
|
||||
background: url(../../../assets/img/rawTopBg.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
&__rawTopBg {
|
||||
background: url(../../../assets/img/rawTopBg.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
&__opLargeBg {
|
||||
background: url(../../../assets/img/opLargeBg.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
// &__left {
|
||||
// background: url(../../../../../../../assets/img/left.png) no-repeat;
|
||||
// background-size: 100% 100%;
|
||||
@@ -262,4 +293,41 @@ export default {
|
||||
.container-body {
|
||||
flex: 1;
|
||||
}
|
||||
.tab-group {
|
||||
display: inline-flex;
|
||||
position: absolute;
|
||||
right: 5%;
|
||||
top: 10%;
|
||||
z-index: 9999;
|
||||
align-items: center;
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
gap: 8px; // Tab之间的间距
|
||||
}
|
||||
|
||||
// Tab基础样式(统一)
|
||||
.tab-item {
|
||||
padding: 0 24px;
|
||||
width: 79px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
border-radius: 12px;
|
||||
transition: all 0.2s ease; // 样式切换动画
|
||||
}
|
||||
|
||||
// 未激活的Tab样式(原first-child样式)
|
||||
.tab-item:not(.active) {
|
||||
background: #ECF4FE;
|
||||
color: #0B58FF;
|
||||
}
|
||||
|
||||
// 激活的Tab样式(原last-child样式)
|
||||
.tab-item.active {
|
||||
background: #3071FF;
|
||||
color: #F9FCFF;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="coreItem">
|
||||
<div class="item" v-for="(item, index) in itemList" :key="index">
|
||||
<div @click="handleRoute(item.path)" class="item" v-for="(item, index) in itemList" :key="index">
|
||||
<div class="unit">{{ item.unit }}</div>
|
||||
<div class="item-content">
|
||||
<div class="content-wrapper">
|
||||
@@ -60,15 +60,22 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleRoute(path) {
|
||||
this.$router.push({
|
||||
path:path
|
||||
})
|
||||
},
|
||||
transformData(rawData) {
|
||||
const dataMap = [
|
||||
{
|
||||
key: 'increase',
|
||||
unit: '本月增效额·万元'
|
||||
unit: '本月增效额·万元',
|
||||
path:'/procurementGainAnalysis/procurementGainAnalysis'
|
||||
},
|
||||
{
|
||||
key: 'totalIncrease',
|
||||
unit: '累计增效额·万元'
|
||||
unit: '累计增效额·万元',
|
||||
path: '/procurementGainAnalysis/procurementGainAnalysis'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -80,7 +87,8 @@ export default {
|
||||
unit: itemInfo.unit,
|
||||
targetValue: rawItem.target || 0,
|
||||
currentValue: rawItem.real || 0,
|
||||
progress: progress
|
||||
progress: progress,
|
||||
path: itemInfo.path
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@@ -108,7 +108,7 @@ export default {
|
||||
case 2: // 月模式,显示“月”
|
||||
return `${dateParts[1]}月`;
|
||||
case 3: // 年模式,显示“年”
|
||||
return `${dateParts[0]}年`;
|
||||
return `${dateParts[1]}月`;
|
||||
default: // 默认月模式
|
||||
return `${dateParts[1]}月`;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
// 定义按钮与 line 数据中 key 的映射关系
|
||||
buttonToDataKey: [
|
||||
'营业收入',
|
||||
'经营收入', // 注意:数据中的 key 是“经营收入”,按钮显示的是“经营性利润”
|
||||
'经营性利润', // 注意:数据中的 key 是“经营收入”,按钮显示的是“经营性利润”
|
||||
'利润总额',
|
||||
'毛利率'
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<header class="report-header" :class="['report-header__' + size]">
|
||||
<!-- 左侧区域:标题 -->
|
||||
<div class="left-content">
|
||||
<div class="left-content" :style="{ marginLeft: leftMargin }">
|
||||
<div class="top-title">{{ topTitle }}</div>
|
||||
</div>
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
|
||||
<!-- 时间选择区域:日/月/年按钮 + 日期选择器 -->
|
||||
<div class="timeType">
|
||||
<div class="item" v-for="(item, index) in timeTypes" :key="index" @click="activeTime = index"
|
||||
<!-- <div class="item" v-for="(item, index) in timeTypes" :key="index" @click="activeTime = index"
|
||||
:class="{ 'no-skew': activeTime === index }">
|
||||
<span class="item-text">{{ item.text }}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="dateP">
|
||||
<div class="label">
|
||||
<span class="label-text">日期选择</span>
|
||||
<span class="label-text">月份选择</span>
|
||||
</div>
|
||||
<el-date-picker v-model="date" :type="getPickerType" :placeholder="getPickerPlaceholder"
|
||||
<el-date-picker v-model="date" type="month" placeholder="请选择月份"
|
||||
class="custom-date-picker" value-format="yyyy-MM-dd" :clearable="false" style="width: 132px;height: 29px;"
|
||||
@change="emitTimeRange" />
|
||||
</div>
|
||||
@@ -43,6 +43,10 @@ export default {
|
||||
isFullScreen: { type: Boolean, default: false },
|
||||
topTitle: { type: String, default: '' },
|
||||
size: { type: String, default: 'basic' },
|
||||
leftMargin: {
|
||||
type: [String, Number],
|
||||
default: '350px' // 默认值设为350px
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -50,20 +54,20 @@ export default {
|
||||
timeTimer: null,
|
||||
date: undefined, // 存储选择的日期(字符串格式:yyyy-MM-dd/yyyy-MM/yyyy)
|
||||
activeTime: 1, // 默认月维度(0=日,1=月,2=年)
|
||||
timeTypes: [
|
||||
{ text: '日', pickerType: 'date', placeholder: '选择日期' },
|
||||
{ text: '月', pickerType: 'month', placeholder: '选择月份' },
|
||||
{ text: '年', pickerType: 'year', 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;
|
||||
}
|
||||
// getPickerType() {
|
||||
// return this.timeTypes[this.activeTime].pickerType;
|
||||
// },
|
||||
// getPickerPlaceholder() {
|
||||
// return this.timeTypes[this.activeTime].placeholder;
|
||||
// }
|
||||
},
|
||||
watch: {
|
||||
activeTime(newVal, oldVal) {
|
||||
@@ -98,49 +102,102 @@ export default {
|
||||
* 年:当年1月1日00:00:00 → 次年1月1日00:00:00
|
||||
*/
|
||||
calculateTimeRange() {
|
||||
// 固定为月维度
|
||||
const mode = 2;
|
||||
// 初始化时间戳为0(兜底值)
|
||||
let startTime = 0;
|
||||
let endTime = 0;
|
||||
const mode = this.activeTime + 1; // 1=日,2=月,3=年
|
||||
const defaultMoment = moment(); // 默认当前时间
|
||||
// 存储目标月份(仅数字,如10、12)
|
||||
let targetMonth = '';
|
||||
// 默认当前月份
|
||||
const defaultMoment = moment();
|
||||
|
||||
const targetMoment = this.date
|
||||
? moment(this.date, this.getPickerType === 'date' ? 'YYYY-MM-DD' : (this.getPickerType === 'month' ? 'YYYY-MM' : 'YYYY'))
|
||||
: defaultMoment;
|
||||
try {
|
||||
// 仅处理月份维度:固定使用YYYY-MM格式解析日期
|
||||
let targetMoment = this.date
|
||||
? moment(this.date, 'YYYY-MM') // 解析传入的月份(如"2025-10")
|
||||
: defaultMoment;
|
||||
|
||||
if (!targetMoment.isValid()) {
|
||||
console.error('无效日期:', this.date);
|
||||
return { startTime, endTime, mode };
|
||||
}
|
||||
// 验证日期是否有效,无效则使用当前月份兜底
|
||||
if (!targetMoment.isValid()) {
|
||||
console.warn('无效的月份格式(请使用YYYY-MM),已使用当前月份:', this.date);
|
||||
targetMoment = defaultMoment;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
// 仅获取月份(数字格式,如10、12;若要两位格式如01、10,使用'MM')
|
||||
targetMonth = targetMoment.format('M'); // 'M'是1-12,'MM'是01-12,可按需选择
|
||||
|
||||
// 2. 月维度:当月1日00:00:00 → 当月最后一天23:59:59(无毫秒)
|
||||
else if (this.activeTime === 1) {
|
||||
// 打印仅含月份的结果
|
||||
console.log('targetMonth', targetMonth);
|
||||
|
||||
// 计算当月第一天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
|
||||
// });
|
||||
} catch (error) {
|
||||
console.error('计算月份时间范围时出错:', error);
|
||||
}
|
||||
|
||||
// 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('时间范围计算结果:', {
|
||||
// 返回月份相关的所有信息:时间戳、维度、仅月份值
|
||||
return {
|
||||
startTime,
|
||||
endTime,
|
||||
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 };
|
||||
targetMonth // 现在仅为月份数字,如"10"
|
||||
};
|
||||
},
|
||||
// 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() {
|
||||
@@ -182,7 +239,7 @@ export default {
|
||||
/* 左侧标题区域 */
|
||||
.left-content {
|
||||
margin-top: 11px;
|
||||
margin-left: 350px;
|
||||
// margin-left: 350px;
|
||||
height: 55px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -252,7 +309,7 @@ export default {
|
||||
}
|
||||
|
||||
.dateP .label {
|
||||
width: 70px;
|
||||
width: 165px;
|
||||
height: 28px;
|
||||
background: rgba(236, 244, 254, 1);
|
||||
transform: skew(-25deg);
|
||||
@@ -319,14 +376,14 @@ export default {
|
||||
::v-deep .custom-date-picker {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
width: 132px !important;
|
||||
width: 165px !important;
|
||||
height: 28px !important;
|
||||
position: relative;
|
||||
margin: 0 !important;
|
||||
|
||||
.el-input__inner {
|
||||
height: 28px !important;
|
||||
width: 132px !important;
|
||||
width: 165px !important;
|
||||
text-align: center;
|
||||
padding-left: 15px !important;
|
||||
padding-right: 32px !important;
|
||||
|
||||
@@ -104,10 +104,10 @@ export default {
|
||||
transformData(rawData) {
|
||||
// 定义指标映射关系,包括名称、对应的数据键和路由
|
||||
const Mapping = [
|
||||
{ key: 'operatingRevenue', name: '营业收入·万元', route: 'operatingRevenue' },
|
||||
{ key: 'operatingIncome', name: '经营性利润·万元', route: 'profitAnalysis' },
|
||||
{ key: 'totalProfit', name: '利润总额·万元', route: 'profitAnalysis' },
|
||||
{ key: 'grossMargin', name: '毛利率·%', route: 'profitAnalysis' }
|
||||
{ key: 'operatingRevenue', name: '营业收入·万元', route: '/operatingRevenue/operatingRevenueIndex' },
|
||||
{ key: 'operatingIncome', name: '经营性利润·万元', route: '/operatingProfit/operatingProfit' },
|
||||
{ key: 'totalProfit', name: '利润总额·万元', route: '/totalProfit/totalProfit' },
|
||||
{ key: 'grossMargin', name: '毛利率·%', route: '/grossMargin/grossMargin' }
|
||||
];
|
||||
|
||||
// 遍历映射关系,转换数据
|
||||
|
||||
@@ -74,10 +74,10 @@ export default {
|
||||
transformData(rawData) {
|
||||
// 定义销售指标映射关系(键名、显示名称、单位、路由路径)
|
||||
const saleMapping = [
|
||||
{ key: 'unitPrice', unit: '单价·元/㎡', path: '/cost/cost' },
|
||||
{ key: 'netPrice', unit: '净价·元/㎡', path: '/cost/cost' },
|
||||
{ key: 'sales', unit: '销量·万㎡', path: 'PSIAnal' },
|
||||
{ key: 'panel', unit: '双镀面板·万㎡', path: 'PSIAnal' }
|
||||
{ key: 'unitPrice', unit: '单价·元/㎡', path: '/unitPriceAnalysis/unitPriceAnalysis' },
|
||||
{ key: 'netPrice', unit: '净价·元/㎡', path: '/netPriceAnalysis/netPriceAnalysis' },
|
||||
{ key: 'sales', unit: '销量·万㎡', path: '/salesVolumeAnalysis/salesVolumeAnalysis' },
|
||||
{ key: 'panel', unit: '双镀面板·万㎡', path: '/salesVolumeAnalysis/salesVolumeAnalysis' }
|
||||
];
|
||||
|
||||
// 遍历映射关系,转换数据
|
||||
@@ -89,7 +89,7 @@ export default {
|
||||
unit: mappingItem.unit,
|
||||
targetValue: indicatorData.target, // 目标值
|
||||
currentValue: indicatorData.real, // 实际值
|
||||
progress: Math.round(indicatorData.rate * 100), // 完成率(百分比,四舍五入)
|
||||
progress: indicatorData.rate ? Math.round(indicatorData.rate) : 0, // 完成率(百分比,四舍五入)
|
||||
path: mappingItem.path // 路由路径
|
||||
};
|
||||
});
|
||||
|
||||
@@ -73,27 +73,27 @@ export default {
|
||||
{
|
||||
key: 'totalCost',
|
||||
unit: '总成本·元/㎡',
|
||||
route: 'cost/cost'
|
||||
route: '/productionCostAnalysis/productionCostAnalysis'
|
||||
},
|
||||
{
|
||||
key: 'rawCost',
|
||||
unit: '原片成本·元/㎡',
|
||||
route: 'cost/cost'
|
||||
route: '/productionCostAnalysis/originalSheetCost'
|
||||
},
|
||||
{
|
||||
key: 'processCost',
|
||||
unit: '加工成本·元/㎡',
|
||||
route: 'cost/cost'
|
||||
route: '/productionCostAnalysis/processingCostAnalysis'
|
||||
},
|
||||
{
|
||||
key: 'rawYield',
|
||||
unit: '原片成品率·%',
|
||||
route: '' // 假设这个没有路由
|
||||
route: '/rawSheetYield/rawSheetYield' // 假设这个没有路由
|
||||
},
|
||||
{
|
||||
key: 'ioYield',
|
||||
unit: '投入产出率·%',
|
||||
route: '' // 假设这个没有路由
|
||||
route: '/inputOutputRatio/inputOutputRatio' // 假设这个没有路由
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user