Files
klp-mono/apps/hand-factory/components/klp-product-statistic/klp-product-statistic.vue
2025-10-29 15:38:20 +08:00

465 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<!-- 日月年汇总单选 -->
<view class="tab-container">
<view
v-for="item in timeTabs"
:key="item.value"
class="tab-item"
:class="{ active: activeTab === item.value }"
@click="handleTabChange(item.value)"
>
{{ item.label }}
</view>
</view>
<!-- 日期选择器 -->
<view class="date-picker">
<!-- 日模式 -->
<view v-if="activeTab === 'day'" class="date-item">
<picker
mode="date"
:value="startDate"
@change="handleDateChange"
>
<view class="picker-text">选择日期{{ startDate }}</view>
</picker>
</view>
<!-- 月模式 -->
<view v-else-if="activeTab === 'month'" class="date-range">
<view class="date-item">
<picker
mode="date"
fields="month"
:value="startDate"
@change="handleStartMonthChange"
>
<view class="picker-text">开始月份{{ startDate }}</view>
</picker>
</view>
<view class="date-item">
<picker
mode="date"
fields="month"
:value="endDate"
:start="startDate"
:end="maxMonthEnd"
@change="handleEndMonthChange"
>
<view class="picker-text">结束月份{{ endDate }}</view>
</picker>
</view>
</view>
<!-- 年模式 -->
<view v-else class="date-range">
<view class="date-item">
<picker
mode="date"
fields="year"
:value="startDate"
@change="handleStartYearChange"
>
<view class="picker-text">开始年份{{ startDate }}</view>
</picker>
</view>
<view class="date-item">
<picker
mode="date"
fields="year"
:value="endDate"
@change="handleEndYearChange"
>
<view class="picker-text">结束年份{{ endDate }}</view>
</picker>
</view>
</view>
</view>
<!-- 统计图轮播产量相关 -->
<view class="chart-container">
<swiper
class="chart-swiper"
:current="currentSwiperIndex"
@change="onSwiperChange"
:autoplay="false"
:circular="false"
:indicator-dots="true"
indicator-active-color="#1a73e8"
indicator-color="#e5e5e5"
>
<swiper-item v-for="(item, index) in chanLiangChartConfig" :key="index">
<qiun-data-charts :type="item.type" :chartData="item.chartData" :title="item.title"/>
</swiper-item>
</swiper>
</view>
<!-- 日期范围汇总 -->
<klp-collapse-panel :title="'汇总(' + displayDateRange + ''">
<k-metric-card
:items="summaryData"
:columns="3"
/>
<!-- 汇总饼图轮播 -->
<swiper
class="chart-swiper"
:autoplay="false"
:circular="false"
:indicator-dots="true"
indicator-active-color="#1a73e8"
indicator-color="#e5e5e5"
>
<swiper-item v-for="(item, index) in summaryChartConfig" :key="index">
<qiun-data-charts type="pie" :opts="item.opts" :chartData="item.chartData" :title="item.title"/>
</swiper-item>
</swiper>
</klp-collapse-panel>
</view>
</template>
<script>
// 2. 独立工具函数避免Vue2 data初始化时调用this.methods的问题
/**
* 获取默认日期(根据视图类型)
* @param {string} type - 视图类型day/month/year
* @returns {string} 格式化后的日期
*/
function getDefaultDate(type = "day") {
const date = new Date();
return formatDate(date, type);
}
/**
* 格式化日期
* @param {Date} date - 日期对象
* @param {string} type - 视图类型day/month/year
* @returns {string} 格式化后的日期
*/
function formatDate(date, type) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
switch (type) {
case "day": return `${year}-${month}-${day}`;
case "month": return `${year}-${month}`;
case "year": return `${year}`;
default: return `${year}-${month}-${day}`;
}
}
export default {
// 4. 响应式数据(替代 Vue3 的 ref
data() {
return {
// 激活的视图类型(日/月/年)
activeTab: "day",
// 开始/结束日期(月份/年份)
startDate: getDefaultDate(),
endDate: getDefaultDate(),
// 视图切换选项
timeTabs: [
{ label: "日视图", value: "day" },
{ label: "月视图", value: "month" },
{ label: "年视图", value: "year" }
],
// 产量相关图表配置(轮播)
chanLiangChartConfig: [
{
title: "规格曲线",
type: "column",
chartData: {} // 初始空对象,后续加载数据
},
{
title: "超产-欠产统计",
type: "line",
chartData: {} // 初始空对象,后续加载数据
},
{
title: "产量-成材率统计",
type: "column",
chartData: {
categories: ["2016", "2017", "2018", "2019", "2020", "2021"],
series: [
{ name: "目标值", data: [35, 36, 31, 33, 13, 34] },
{ name: "完成量", data: [18, 27, 21, 24, 6, 28] }
]
} // 固定初始数据
}
],
// 汇总饼图配置(轮播)
summaryChartConfig: [
{
title: "成品厚度统计(mm)",
opts: {
color: ["#1890FF","#91CB74","#FAC858","#EE6666","#73C0DE","#3CA272","#FC8452","#9A60B4","#ea7ccc"],
padding: [5,5,5,5],
enableScroll: false,
position: "left",
float: "left",
extra: {
pie: {
activeOpacity: 0.5,
activeRadius: 10,
offsetAngle: 0,
labelWidth: 15,
border: false,
borderWidth: 3,
borderColor: "#FFFFFF"
}
}
},
chartData: JSON.parse(JSON.stringify({
series: [{ data: [{"name":"一班","value":50},{"name":"二班","value":30},{"name":"三班","value":20},{"name":"四班","value":18},{"name":"五班","value":8}] }]
}))
},
{
title: "钢种统计",
opts: {
color: ["#1890FF","#91CB74","#FAC858","#EE6666","#73C0DE","#3CA272","#FC8452","#9A60B4","#ea7ccc"],
padding: [5,5,5,5],
enableScroll: false,
extra: {
pie: {
activeOpacity: 0.5,
activeRadius: 10,
offsetAngle: 0,
labelWidth: 15,
border: false,
borderWidth: 3,
borderColor: "#FFFFFF"
}
}
},
chartData: JSON.parse(JSON.stringify({
series: [{ data: [{"name":"一班","value":50},{"name":"二班","value":30},{"name":"三班","value":20},{"name":"四班","value":18},{"name":"五班","value":8}] }]
}))
},
{
title: "班组统计",
opts: {
color: ["#1890FF","#91CB74","#FAC858","#EE6666","#73C0DE","#3CA272","#FC8452","#9A60B4","#ea7ccc"],
padding: [5,5,5,5],
enableScroll: false,
extra: {
pie: {
activeOpacity: 0.5,
activeRadius: 10,
offsetAngle: 0,
labelWidth: 15,
border: false,
borderWidth: 3,
borderColor: "#FFFFFF"
}
}
},
chartData: JSON.parse(JSON.stringify({
series: [{ data: [{"name":"一班","value":50},{"name":"二班","value":30},{"name":"三班","value":20},{"name":"四班","value":18},{"name":"五班","value":8}] }]
}))
}
],
// 轮播图当前索引
currentSwiperIndex: 0,
// 服务器加载的图表基础数据
chartData: {},
// 汇总指标卡数据
summaryData: [
{ label: "生产钢卷数", value: 186 },
{ label: "平均宽度", value: 1054, unit: "mm" },
{ label: "平均厚度", value: 0.93, unit: "mm" },
{ label: "原料总量", value: 3396.47, unit: "t" },
{ label: "成品总量", value: 4360.18, unit: "t" },
{ label: "成材率", value: 95.99, unit: "%" } // 原代码unit写t推测是笔误修正为%
]
};
},
// 5. 计算属性(替代 Vue3 的 computed 函数)
computed: {
// 月模式:最大结束月份(开始月份+1年
maxMonthEnd() {
if (!this.startDate) return "";
const date = new Date(this.startDate);
date.setFullYear(date.getFullYear() + 1);
return formatDate(date, "month");
},
// 日期范围展示文本(汇总标题用)
displayDateRange() {
switch (this.activeTab) {
case "day":
return this.startDate;
case "month":
return `${this.startDate}${this.endDate}`;
case "year":
return `${this.startDate}${this.endDate}`;
default:
return "";
}
}
},
// 6. 生命周期钩子(替代 Vue3 的 onMounted
mounted() {
this.getServerData(); // 页面挂载后加载图表数据
},
// 7. 方法定义(所有交互逻辑与数据处理)
methods: {
// 切换视图(日/月/年)
handleTabChange(tab) {
this.activeTab = tab;
// 重置日期:日视图首尾一致,月/年视图首尾默认当前
const defaultDate = getDefaultDate();
this.startDate = defaultDate;
this.endDate = tab === "day" ? defaultDate : getDefaultDate(tab);
},
// 日模式:日期选择器变更
handleDateChange(e) {
this.startDate = e.detail.value;
this.endDate = e.detail.value; // 日视图首尾日期同步
},
// 月模式:开始月份变更
handleStartMonthChange(e) {
this.startDate = e.detail.value;
// 自动调整结束月份:不超过开始月份+1年
const maxEndDate = new Date(this.startDate);
maxEndDate.setFullYear(maxEndDate.getFullYear() + 1);
const maxEndStr = formatDate(maxEndDate, "month");
if (new Date(this.endDate) > maxEndDate) {
this.endDate = maxEndStr;
}
},
// 月模式:结束月份变更
handleEndMonthChange(e) {
this.endDate = e.detail.value;
},
// 年模式:开始年份变更
handleStartYearChange(e) {
this.startDate = e.detail.value;
},
// 年模式:结束年份变更
handleEndYearChange(e) {
this.endDate = e.detail.value;
},
// 轮播图切换回调(更新当前索引)
onSwiperChange(e) {
this.currentSwiperIndex = e.detail.current;
},
// 模拟从服务器加载图表数据
getServerData() {
setTimeout(() => {
// 模拟服务器返回数据
const serverRes = {
categories: ["2016", "2017", "2018", "2019", "2020", "2021"],
series: [
{ name: "目标值", data: [35, 36, 31, 33, 13, 34] },
{ name: "完成量", data: [18, 27, 21, 24, 6, 28] }
]
};
// 深拷贝避免引用问题确保Vue2响应式
this.chartData = JSON.parse(JSON.stringify(serverRes));
// 更新产量图表配置的前两个图表数据(第三个固定)
this.chanLiangChartConfig.forEach((item, index) => {
if (index < 2) { // 仅前两个图表用服务器数据
item.chartData = JSON.parse(JSON.stringify(this.chartData));
}
});
}, 500);
}
}
};
</script>
<style scoped>
.container {
padding: 20rpx;
}
/* 简洁Tab容器 */
.tab-container {
display: flex;
box-sizing: border-box;
background: #fff;
margin-bottom: 20rpx;
border-radius: 12rpx;
overflow: hidden;
border: 1rpx solid #e8e8e8;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.tab-item {
flex: 1;
text-align: center;
padding: 24rpx 20rpx;
color: #666;
font-size: 28rpx;
font-weight: 500;
transition: all 0.3s ease;
position: relative;
&:not(:last-child)::after {
content: '';
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 1rpx;
height: 40rpx;
background: #e8e8e8;
}
}
.tab-item.active {
color: #fff;
background: #1a73e8;
font-weight: 600;
}
/* 日期选择器 */
.date-picker {
padding: 20rpx;
background: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
border: 1rpx solid #e8e8e8;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.date-range {
display: flex;
gap: 20rpx;
}
.date-item {
flex: 1;
}
.picker-text {
padding: 20rpx;
background: #f8f9fa;
border-radius: 10rpx;
color: #333;
font-size: 28rpx;
border: 1rpx solid #e8e8e8;
transition: all 0.2s ease;
&:active {
background: #f0f2f5;
}
}
/* 图表容器 */
.chart-container {
margin-bottom: 20rpx;
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
border: 1rpx solid #e8e8e8;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
}
.chart-swiper {
height: 500rpx;
box-sizing: border-box;
}
</style>