2025-10-27 13:21:43 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view>
|
|
|
|
|
|
<!-- 日月年汇总单选 -->
|
|
|
|
|
|
<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 v-if="activeTab != 'day'">
|
|
|
|
|
|
<qiun-data-charts type="mix" :chartData="chartData" :opts="opts" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view>
|
|
|
|
|
|
<klp-collapse-panel :title="'汇总(' + displayDateRange + ')'">
|
|
|
|
|
|
<k-metric-card
|
|
|
|
|
|
:items="summaryData"
|
|
|
|
|
|
></k-metric-card>
|
|
|
|
|
|
</klp-collapse-panel>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view v-if="activeTab == 'day'">
|
|
|
|
|
|
<klp-collapse-panel title="详细信息">
|
|
|
|
|
|
<k-table :columns="columns" :data="tableData"></k-table>
|
|
|
|
|
|
</klp-collapse-panel>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view style="margin-top: 30rpx;">
|
|
|
|
|
|
<qiun-data-charts type="pie" :chartData="pieChartData" :opts="pieOpts" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2025-10-31 14:50:19 +08:00
|
|
|
|
import { listStoppage } from '@/api/pocket/plantState'
|
|
|
|
|
|
|
2025-10-27 13:21:43 +08:00
|
|
|
|
// 2. 独立工具函数(避免data初始化时调用this.methods的问题)
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取默认日期(根据视图类型)
|
|
|
|
|
|
* @param {string} type - 视图类型:day/month/year
|
|
|
|
|
|
* @returns {string} 格式化后的日期
|
|
|
|
|
|
*/
|
|
|
|
|
|
function getDefaultDate(type = "day") {
|
|
|
|
|
|
const date = new Date();
|
|
|
|
|
|
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}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 格式化日期
|
|
|
|
|
|
* @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" }
|
|
|
|
|
|
],
|
|
|
|
|
|
// 混合图表数据
|
|
|
|
|
|
chartData: {
|
|
|
|
|
|
categories: ["2018", "2019", "2020", "2021", "2022", "2023"],
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "曲面",
|
|
|
|
|
|
type: "area",
|
|
|
|
|
|
style: "curve",
|
|
|
|
|
|
data: [70, 50, 85, 130, 64, 88]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "柱1",
|
|
|
|
|
|
index: 1,
|
|
|
|
|
|
type: "column",
|
|
|
|
|
|
data: [40, { value: 30, color: "#f04864" }, 55, 110, 24, 58]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "柱2",
|
|
|
|
|
|
index: 1,
|
|
|
|
|
|
type: "column",
|
|
|
|
|
|
data: [50, 20, 75, 60, 34, 38]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "曲线",
|
|
|
|
|
|
type: "line",
|
|
|
|
|
|
style: "curve",
|
|
|
|
|
|
color: "#1890ff",
|
|
|
|
|
|
disableLegend: true,
|
|
|
|
|
|
data: [70, 50, 85, 130, 64, 88]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "折线",
|
|
|
|
|
|
type: "line",
|
|
|
|
|
|
color: "#2fc25b",
|
|
|
|
|
|
data: [120, 140, 105, 170, 95, 160]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "点",
|
|
|
|
|
|
index: 2,
|
|
|
|
|
|
type: "point",
|
|
|
|
|
|
color: "#f04864",
|
|
|
|
|
|
data: [100, 80, 125, 150, 112, 132]
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
// 混合图表配置
|
|
|
|
|
|
opts: {
|
|
|
|
|
|
color: [
|
|
|
|
|
|
"#1890FF",
|
|
|
|
|
|
"#91CB74",
|
|
|
|
|
|
"#FAC858",
|
|
|
|
|
|
"#EE6666",
|
|
|
|
|
|
"#73C0DE",
|
|
|
|
|
|
"#3CA272",
|
|
|
|
|
|
"#FC8452",
|
|
|
|
|
|
"#9A60B4",
|
|
|
|
|
|
"#ea7ccc"
|
|
|
|
|
|
],
|
|
|
|
|
|
padding: [15, 15, 0, 15],
|
|
|
|
|
|
enableScroll: false,
|
|
|
|
|
|
legend: {},
|
|
|
|
|
|
xAxis: {
|
|
|
|
|
|
disableGrid: true,
|
|
|
|
|
|
title: "单位:年"
|
|
|
|
|
|
},
|
|
|
|
|
|
yAxis: {
|
|
|
|
|
|
disabled: false,
|
|
|
|
|
|
disableGrid: false,
|
|
|
|
|
|
splitNumber: 5,
|
|
|
|
|
|
gridType: "dash",
|
|
|
|
|
|
dashLength: 4,
|
|
|
|
|
|
gridColor: "#CCCCCC",
|
|
|
|
|
|
padding: 10,
|
|
|
|
|
|
showTitle: true,
|
|
|
|
|
|
data: [
|
|
|
|
|
|
{ position: "left", title: "折线" },
|
|
|
|
|
|
{ position: "right", min: 0, max: 200, title: "柱状图", textAlign: "left" },
|
|
|
|
|
|
{ position: "right", min: 0, max: 200, title: "点", textAlign: "left" }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
extra: { mix: { column: { width: 20 } } }
|
|
|
|
|
|
},
|
|
|
|
|
|
// 汇总数据(供 k-metric-card 使用)
|
|
|
|
|
|
summaryData: [
|
|
|
|
|
|
{ label: "停机时间", value: 730, unit: "min" },
|
|
|
|
|
|
{ label: "停机次数", value: 9 },
|
|
|
|
|
|
{ label: "作业率", value: 49.31, unit: "%" }
|
|
|
|
|
|
],
|
|
|
|
|
|
// 表格列配置(日视图详细信息)
|
|
|
|
|
|
columns: [
|
|
|
|
|
|
{ title: "起止时间", key: "time" },
|
|
|
|
|
|
{ title: "持续时间", key: "duration" },
|
|
|
|
|
|
{ title: "备注", key: "remark" },
|
|
|
|
|
|
{ title: "机组", key: "machine" }
|
|
|
|
|
|
],
|
|
|
|
|
|
// 表格数据(日视图详细信息)
|
|
|
|
|
|
tableData: [
|
|
|
|
|
|
{
|
|
|
|
|
|
time: "2022-01-01 08:00:00",
|
|
|
|
|
|
duration: "30min",
|
|
|
|
|
|
remark: "设备维护",
|
|
|
|
|
|
machine: "1号机"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
time: "2022-01-01 10:00:00",
|
|
|
|
|
|
duration: "20min",
|
|
|
|
|
|
remark: "设备故障",
|
|
|
|
|
|
machine: "2号机"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
time: "2022-01-01 12:00:00",
|
|
|
|
|
|
duration: "40min",
|
|
|
|
|
|
remark: "设备维护",
|
|
|
|
|
|
machine: "3号机"
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
// 饼图数据
|
|
|
|
|
|
pieChartData: {
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
data: [
|
|
|
|
|
|
{ name: "一班", value: 50 },
|
|
|
|
|
|
{ name: "二班", value: 30 },
|
|
|
|
|
|
{ name: "三班", value: 20 },
|
|
|
|
|
|
{ name: "四班", value: 18, labelText: "四班:18人" },
|
|
|
|
|
|
{ name: "五班", value: 8 }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
// 饼图配置
|
|
|
|
|
|
pieOpts: {
|
|
|
|
|
|
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: true,
|
|
|
|
|
|
borderWidth: 3,
|
|
|
|
|
|
borderColor: "#FFFFFF"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
// 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. 方法定义(所有交互逻辑放这里)
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
// 切换视图(日/月/年)
|
|
|
|
|
|
handleTabChange(tab) {
|
|
|
|
|
|
this.activeTab = tab;
|
|
|
|
|
|
// 重置日期:日视图首尾日期相同,月/年视图首尾默认当前
|
|
|
|
|
|
const defaultDate = getDefaultDate();
|
|
|
|
|
|
this.startDate = defaultDate;
|
|
|
|
|
|
this.endDate = tab === "day" ? defaultDate : getDefaultDate(tab);
|
2025-10-31 14:50:19 +08:00
|
|
|
|
// 切换视图时重新加载数据
|
|
|
|
|
|
this.loadStoppageData();
|
2025-10-27 13:21:43 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 日模式:日期选择器变更
|
|
|
|
|
|
handleDateChange(e) {
|
|
|
|
|
|
this.startDate = e.detail.value;
|
|
|
|
|
|
this.endDate = e.detail.value; // 日模式首尾日期一致
|
2025-10-31 14:50:19 +08:00
|
|
|
|
this.loadStoppageData();
|
2025-10-27 13:21:43 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 月模式:开始月份变更
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-10-31 14:50:19 +08:00
|
|
|
|
this.loadStoppageData();
|
2025-10-27 13:21:43 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 月模式:结束月份变更
|
|
|
|
|
|
handleEndMonthChange(e) {
|
|
|
|
|
|
this.endDate = e.detail.value;
|
2025-10-31 14:50:19 +08:00
|
|
|
|
this.loadStoppageData();
|
2025-10-27 13:21:43 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 年模式:开始年份变更
|
|
|
|
|
|
handleStartYearChange(e) {
|
|
|
|
|
|
this.startDate = e.detail.value;
|
2025-10-31 14:50:19 +08:00
|
|
|
|
this.loadStoppageData();
|
2025-10-27 13:21:43 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 年模式:结束年份变更
|
|
|
|
|
|
handleEndYearChange(e) {
|
|
|
|
|
|
this.endDate = e.detail.value;
|
2025-10-31 14:50:19 +08:00
|
|
|
|
this.loadStoppageData();
|
|
|
|
|
|
},
|
|
|
|
|
|
// 加载停机数据
|
|
|
|
|
|
loadStoppageData() {
|
|
|
|
|
|
uni.showLoading({ title: '加载中' })
|
|
|
|
|
|
|
|
|
|
|
|
const queryParams = {
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
pageSize: 100,
|
|
|
|
|
|
startDate: this.startDate,
|
|
|
|
|
|
endDate: this.endDate
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
listStoppage(queryParams).then(response => {
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
|
|
|
|
|
|
if (response.code === 200 && response.rows && response.rows.length > 0) {
|
|
|
|
|
|
// 处理停机数据
|
|
|
|
|
|
console.log('停机数据:', response.rows)
|
|
|
|
|
|
|
|
|
|
|
|
// 更新表格数据(日视图)
|
|
|
|
|
|
this.tableData = response.rows.map(item => ({
|
|
|
|
|
|
time: this.formatDateTime(item.startDate) + ' - ' + this.formatDateTime(item.endDate),
|
|
|
|
|
|
duration: item.duration + 'min',
|
|
|
|
|
|
remark: item.remark || '-',
|
|
|
|
|
|
machine: item.unit || '-'
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
|
|
// 计算汇总数据
|
|
|
|
|
|
const totalDuration = response.rows.reduce((sum, item) => sum + (item.duration || 0), 0)
|
|
|
|
|
|
const totalCount = response.rows.length
|
|
|
|
|
|
|
|
|
|
|
|
this.summaryData = [
|
|
|
|
|
|
{ label: '停机时间', value: totalDuration, unit: 'min' },
|
|
|
|
|
|
{ label: '停机次数', value: totalCount },
|
|
|
|
|
|
{ label: '作业率', value: this.calculateWorkRate(totalDuration), unit: '%' }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 更新饼图数据(按班组统计)
|
|
|
|
|
|
const crewMap = {}
|
|
|
|
|
|
response.rows.forEach(item => {
|
|
|
|
|
|
const crew = item.crew || '未知班组'
|
|
|
|
|
|
crewMap[crew] = (crewMap[crew] || 0) + (item.duration || 0)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
this.pieChartData = {
|
|
|
|
|
|
series: [{
|
|
|
|
|
|
data: Object.keys(crewMap).map(crew => ({
|
|
|
|
|
|
name: crew,
|
|
|
|
|
|
value: crewMap[crew]
|
|
|
|
|
|
}))
|
|
|
|
|
|
}]
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 没有数据时使用默认值
|
|
|
|
|
|
console.log('暂无停机数据')
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
console.error('加载停机数据失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
// 格式化日期时间
|
|
|
|
|
|
formatDateTime(dateStr) {
|
|
|
|
|
|
if (!dateStr) return '-'
|
|
|
|
|
|
const date = new Date(dateStr)
|
|
|
|
|
|
const year = date.getFullYear()
|
|
|
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
|
|
|
const day = String(date.getDate()).padStart(2, '0')
|
|
|
|
|
|
const hours = String(date.getHours()).padStart(2, '0')
|
|
|
|
|
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}`
|
|
|
|
|
|
},
|
|
|
|
|
|
// 计算作业率
|
|
|
|
|
|
calculateWorkRate(stopDuration) {
|
|
|
|
|
|
// 假设一天工作24小时,1440分钟
|
|
|
|
|
|
const totalMinutes = 1440
|
|
|
|
|
|
const workRate = ((totalMinutes - stopDuration) / totalMinutes) * 100
|
|
|
|
|
|
return Math.max(0, Math.min(100, workRate)).toFixed(2)
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
2025-10-31 14:50:19 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 生命周期钩子
|
|
|
|
|
|
mounted() {
|
|
|
|
|
|
this.loadStoppageData()
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2025-10-29 15:38:20 +08:00
|
|
|
|
/* 简洁Tab容器 */
|
2025-10-27 13:21:43 +08:00
|
|
|
|
.tab-container {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
box-sizing: border-box;
|
2025-10-29 15:38:20 +08:00
|
|
|
|
background: #fff;
|
|
|
|
|
|
margin: 20rpx;
|
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
border-radius: 12rpx;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
overflow: hidden;
|
2025-10-29 15:38:20 +08:00
|
|
|
|
border: 1rpx solid #e8e8e8;
|
|
|
|
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tab-item {
|
2025-10-29 15:38:20 +08:00
|
|
|
|
flex: 1;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
text-align: center;
|
2025-10-29 15:38:20 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tab-item.active {
|
2025-10-29 15:38:20 +08:00
|
|
|
|
color: #fff;
|
|
|
|
|
|
background: #1a73e8;
|
|
|
|
|
|
font-weight: 600;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-29 15:38:20 +08:00
|
|
|
|
/* 日期选择器 */
|
2025-10-27 13:21:43 +08:00
|
|
|
|
.date-picker {
|
2025-10-29 15:38:20 +08:00
|
|
|
|
padding: 20rpx;
|
|
|
|
|
|
margin: 0 20rpx 20rpx;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
border: 1rpx solid #e8e8e8;
|
|
|
|
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.date-range {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.date-item {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.picker-text {
|
2025-10-29 15:38:20 +08:00
|
|
|
|
padding: 20rpx;
|
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
|
border-radius: 10rpx;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
border: 1rpx solid #e8e8e8;
|
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
background: #f0f2f5;
|
|
|
|
|
|
}
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|