Files
klp-mono/apps/hand-factory/components/klp-shutdown-statistic/klp-shutdown-statistic.vue
2025-11-25 14:01:47 +08:00

1022 lines
28 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="page-container">
<!-- 时间维度切换 -->
<view class="time-tab-bar">
<view
v-for="item in timeTabs"
:key="item.value"
class="time-tab-item"
:class="{ 'time-tab-active': activeTab === item.value }"
@click="handleTabChange(item.value)"
>
{{ item.label }}
</view>
</view>
<!-- 日期选择区 -->
<view class="date-selector">
<!-- 日模式 -->
<picker v-if="activeTab === 'day'" mode="date" :value="startDate" @change="handleDateChange">
<view class="date-input">
<text class="date-label">日期</text>
<text class="date-value">{{ startDate }}</text>
</view>
</picker>
<!-- 月模式 -->
<view v-else-if="activeTab === 'month'" class="date-range-group">
<picker mode="date" fields="month" :value="startDate" @change="handleStartMonthChange">
<view class="date-input">
<text class="date-label"></text>
<text class="date-value">{{ startDate }}</text>
</view>
</picker>
<view class="date-separator"></view>
<picker mode="date" fields="month" :value="endDate" :start="startDate" :end="maxMonthEnd" @change="handleEndMonthChange">
<view class="date-input">
<text class="date-label"></text>
<text class="date-value">{{ endDate }}</text>
</view>
</picker>
</view>
<!-- 年模式 -->
<view v-else class="date-range-group">
<picker mode="date" fields="year" :value="startDate" @change="handleStartYearChange">
<view class="date-input">
<text class="date-label"></text>
<text class="date-value">{{ startDate }}</text>
</view>
</picker>
<view class="date-separator"></view>
<picker mode="date" fields="year" :value="endDate" @change="handleEndYearChange">
<view class="date-input">
<text class="date-label"></text>
<text class="date-value">{{ endDate }}</text>
</view>
</picker>
</view>
</view>
<!-- 停机汇总 -->
<view class="summary-section">
<view class="section-header">
<text class="section-title">停机汇总</text>
<text class="section-date">{{ displayDateRange }}</text>
</view>
<view class="summary-grid">
<view class="summary-card" v-for="(item, index) in summaryData" :key="index">
<text class="summary-label">{{ item.label }}</text>
<view class="summary-value-box">
<text class="summary-value">{{ item.value }}</text>
<text v-if="item.unit" class="summary-unit">{{ item.unit }}</text>
</view>
</view>
</view>
</view>
<!-- 停机趋势图/年视图 -->
<view class="chart-section" v-if="activeTab !== 'day' && trendChartData.categories && trendChartData.categories.length > 0">
<view class="section-header">
<text class="section-title">停机趋势</text>
</view>
<view class="chart-wrapper trend-chart">
<qiun-data-charts type="mix" :chartData="trendChartData" :opts="trendChartOpts"/>
</view>
</view>
<!-- 停机分布 - 班组 -->
<view class="chart-section">
<view class="section-header">
<text class="section-title">班组停机分布</text>
</view>
<view class="pie-chart-single" v-if="crewPieData.series[0].data.length > 0">
<qiun-data-charts type="pie" :chartData="crewPieData" :opts="pieChartOpts"/>
</view>
<view class="empty-chart" v-else>
<text class="empty-icon">📊</text>
<text class="empty-text">此时间段未发生停机</text>
</view>
</view>
<!-- 停机分布 - 类型 -->
<view class="chart-section">
<view class="section-header">
<text class="section-title">停机类型分布</text>
</view>
<view class="pie-chart-single" v-if="typePieData.series[0].data.length > 0">
<qiun-data-charts type="pie" :chartData="typePieData" :opts="pieChartOpts"/>
</view>
<view class="empty-chart" v-else>
<text class="empty-icon">📊</text>
<text class="empty-text">此时间段未发生停机</text>
</view>
</view>
<!-- 停机详细列表日视图 -->
<view class="detail-section" v-if="activeTab === 'day'">
<view class="section-header">
<text class="section-title">停机详情</text>
</view>
<view class="detail-list">
<view class="detail-item" v-for="(item, index) in tableData" :key="index">
<view class="detail-header">
<text class="detail-time">{{ item.time }}</text>
<text class="detail-duration">{{ item.duration }}</text>
</view>
<view class="detail-info">
<text class="detail-label">机组</text>
<text class="detail-text">{{ item.machine }}</text>
</view>
<view class="detail-info">
<text class="detail-label">备注</text>
<text class="detail-text">{{ item.remark }}</text>
</view>
</view>
<view v-if="tableData.length === 0" class="empty-state">
<text class="empty-text">暂无停机记录</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { listStoppage } from '@/api/pocket/plantState'
// 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}`;
}
}
// 获取上个月的年月
function getLastMonth() {
const date = new Date();
date.setMonth(date.getMonth() - 1); // 减1个月
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, "0");
return `${year}-${month}`;
}
/**
* 格式化日期
* @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" }
],
// 汇总数据
summaryData: [
{ label: "停机时间", value: 0, unit: "min" },
{ label: "停机次数", value: 0, unit: "次" },
{ label: "作业率", value: 0, unit: "%" }
],
// 停机趋势图(月/年视图)
trendChartData: {
categories: [],
series: []
},
trendChartOpts: {
color: ["#0066cc", "#f56c6c"],
padding: [15, 15, 0, 15],
enableScroll: false,
legend: { position: "top" },
dataLabel: false, // 隐藏数据标签,避免数字叠在一起
xAxis: {
disableGrid: true,
rotateLabel: true,
rotateAngle: 60,
labelCount: 10,
fontSize: 10
},
yAxis: {
gridType: "dash",
dashLength: 4,
gridColor: "#e4e7ed",
data: [
{ position: "left", title: "停机时间(min)" },
{ position: "right", title: "作业率(%)" }
]
},
extra: {
mix: {
column: {
width: 40,
categoryGap: 10
}
},
tooltip: {
showBox: true,
showArrow: true,
showCategory: true
}
}
},
// 饼图配置
pieChartOpts: {
color: ["#0066cc", "#409eff", "#66b1ff", "#a0cfff", "#d9ecff"],
padding: [15, 15, 15, 15],
enableScroll: false,
legend: {
show: true,
position: "bottom",
lineHeight: 16,
fontSize: 10,
fontColor: "#666",
margin: 5,
itemGap: 8
},
extra: {
pie: {
activeOpacity: 0.5,
activeRadius: 10,
labelWidth: 15,
border: false,
ringWidth: 0,
offsetAngle: 0,
disablePieStroke: true
}
}
},
// 班组停机分布
crewPieData: {
series: [{ data: [] }]
},
// 停机类型分布
typePieData: {
series: [{ data: [] }]
},
// 停机详细列表
tableData: []
};
},
// 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;
// 重置日期
if (tab === "day") {
// 日视图:首尾日期相同(今天)
const today = getDefaultDate();
this.startDate = today;
this.endDate = today;
} else if (tab === "month") {
// 月视图:上个月到这个月
this.startDate = getLastMonth();
this.endDate = getDefaultDate("month");
} else {
// 年视图:今年
const currentYear = getDefaultDate("year");
this.startDate = currentYear;
this.endDate = currentYear;
}
// 切换视图时重新加载数据
this.loadStoppageData();
},
// 日模式:日期选择器变更
handleDateChange(e) {
this.startDate = e.detail.value;
this.endDate = e.detail.value; // 日模式首尾日期一致
this.loadStoppageData();
},
// 月模式:开始月份变更
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;
}
this.loadStoppageData();
},
// 月模式:结束月份变更
handleEndMonthChange(e) {
this.endDate = e.detail.value;
this.loadStoppageData();
},
// 年模式:开始年份变更
handleStartYearChange(e) {
this.startDate = e.detail.value;
this.loadStoppageData();
},
// 年模式:结束年份变更
handleEndYearChange(e) {
this.endDate = e.detail.value;
this.loadStoppageData();
},
// 加载停机数据
loadStoppageData() {
uni.showLoading({ title: '加载中' })
// 转换为完整日期格式
const start = this.formatFullDate(this.startDate, true)
let end = this.formatFullDate(this.endDate, false)
// 如果是月视图,检查结束日期是否是当月
if (this.activeTab === 'month' && this.endDate && this.endDate.length === 7) {
const today = new Date()
const todayYear = today.getFullYear()
const todayMonth = today.getMonth() + 1
const [endYear, endMonth] = this.endDate.split('-').map(Number)
// 如果是当月,使用今天的日期作为结束日期
if (endYear === todayYear && endMonth === todayMonth) {
const todayDay = today.getDate()
end = `${this.endDate}-${String(todayDay).padStart(2, '0')}`
}
// 如果不是当月end 已经是该月的最后一天(由 formatFullDate 处理)
}
const queryParams = {
pageNum: 1,
pageSize: 9999,
startDate: start,
endDate: end
}
console.log('停机查询参数:', queryParams)
listStoppage(queryParams).then(response => {
uni.hideLoading()
console.log('停机统计响应:', response)
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: this.secondsToMinutes(item.duration) + 'min',
remark: item.remark || '-',
machine: item.unit || '-'
}))
// 计算汇总数据(秒转分钟)
const totalDurationSeconds = response.rows.reduce((sum, item) => sum + (Number(item.duration) || 0), 0)
const totalDurationMinutes = this.secondsToMinutes(totalDurationSeconds)
const totalCount = response.rows.length
// 计算作业率(需要知道总可用时间)
const totalAvailableMinutes = this.getTotalAvailableMinutes()
const workRate = this.calculateWorkRate(totalDurationMinutes, totalAvailableMinutes)
this.summaryData = [
{ label: '停机时间', value: totalDurationMinutes, unit: 'min' },
{ label: '停机次数', value: totalCount, unit: '次' },
{ label: '作业率', value: workRate, unit: '%' }
]
// 更新饼图数据(按班组统计)
const crewMap = {}
const typeMap = {}
response.rows.forEach(item => {
const crew = item.crew || '未知班组'
const type = item.stopType || '未知类型'
const durationMinutes = this.secondsToMinutes(item.duration)
crewMap[crew] = (crewMap[crew] || 0) + durationMinutes
typeMap[type] = (typeMap[type] || 0) + durationMinutes
})
this.crewPieData = {
series: [{
data: Object.keys(crewMap).map(crew => ({
name: crew,
value: crewMap[crew]
}))
}]
}
this.typePieData = {
series: [{
data: Object.keys(typeMap).map(type => ({
name: type,
value: typeMap[type]
}))
}]
}
// 如果是月/年视图,构建趋势图
if (this.activeTab !== 'day') {
if (response.rows.length > 0) {
this.buildTrendChart(response.rows)
} else {
// 没有数据时也要清空趋势图
this.trendChartData = {
categories: [],
series: []
}
}
}
} else {
// 没有数据时使用默认值
console.log('暂无停机数据')
// 清空数据
this.tableData = []
this.summaryData = [
{ label: '停机时间', value: 0, unit: 'min' },
{ label: '停机次数', value: 0, unit: '次' },
{ label: '作业率', value: 100, unit: '%' }
]
this.crewPieData = { series: [{ data: [] }] }
this.typePieData = { series: [{ data: [] }] }
// 清空趋势图
this.trendChartData = {
categories: [],
series: []
}
}
}).catch(error => {
uni.hideLoading()
console.error('加载停机数据失败:', error)
uni.showToast({
title: '加载失败',
icon: 'none'
})
})
},
// 格式化日期时间
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}`
},
// 获取总可用时间(分钟)
getTotalAvailableMinutes() {
const start = new Date(this.formatFullDate(this.startDate, true))
const end = new Date(this.formatFullDate(this.endDate, false))
// 计算天数差
const diffTime = end - start
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1 // 包含结束日期
// 每天1440分钟24小时
return diffDays * 1440
},
// 计算作业率
calculateWorkRate(stopDuration, totalAvailableMinutes) {
if (!totalAvailableMinutes || totalAvailableMinutes === 0) {
return 100
}
const workRate = ((totalAvailableMinutes - stopDuration) / totalAvailableMinutes) * 100
return Math.max(0, Math.min(100, workRate)).toFixed(2)
},
// 构建趋势图(月/年视图)
buildTrendChart(stoppageData) {
if (!stoppageData || stoppageData.length === 0) {
console.log('无法构建趋势图:数据为空')
this.trendChartData = {
categories: [],
series: []
}
return
}
// 根据视图类型决定分组方式
const dateMap = {}
const isYearView = this.activeTab === 'year'
stoppageData.forEach(item => {
if (!item.startDate) return
let key
if (isYearView) {
// 年视图:按月份分组
key = this.formatDateByMonth(item.startDate)
} else {
// 月视图:按日期分组
key = this.formatDate(item.startDate)
}
if (!dateMap[key]) {
dateMap[key] = { duration: 0, count: 0 }
}
const durationMinutes = this.secondsToMinutes(item.duration)
dateMap[key].duration += durationMinutes
dateMap[key].count += 1
})
let categories = []
if (isYearView) {
// 年视图生成完整的12个月份
categories = Array.from({ length: 12 }, (_, i) => `${i + 1}`)
} else {
// 月视图:生成从开始日期到结束日期的完整日期范围
const startStr = this.formatFullDate(this.startDate, true)
let endStr = this.formatFullDate(this.endDate, false)
// 检查结束日期是否是当月
const today = new Date()
const todayYear = today.getFullYear()
const todayMonth = today.getMonth() + 1
// 解析结束日期格式yyyy-MM
if (this.endDate && this.endDate.length === 7) {
const [endYear, endMonth] = this.endDate.split('-').map(Number)
// 如果是当月,使用今天的日期作为结束日期
if (endYear === todayYear && endMonth === todayMonth) {
const todayDay = today.getDate()
endStr = `${this.endDate}-${String(todayDay).padStart(2, '0')}`
}
// 如果不是当月endStr 已经是该月的最后一天(由 formatFullDate 处理)
}
const start = new Date(startStr)
const end = new Date(endStr)
// 生成所有日期
const dateList = []
const currentDate = new Date(start)
while (currentDate <= end) {
// 格式化日期为 M/D 格式
const month = currentDate.getMonth() + 1
const day = currentDate.getDate()
const dateKey = `${month}/${day}`
dateList.push(dateKey)
// 移动到下一天
currentDate.setDate(currentDate.getDate() + 1)
}
categories = dateList
}
if (categories.length === 0) {
console.log('无法构建趋势图:无有效日期')
this.trendChartData = {
categories: [],
series: []
}
return
}
const durationData = []
const rateData = []
categories.forEach(key => {
const data = dateMap[key] || { duration: 0, count: 0 }
durationData.push(data.duration)
// 计算作业率
// 年视图:每个月按实际天数计算(根据选择的年份)
// 月视图每天1440分钟
let totalMinutes
if (isYearView) {
// 获取选择的年份
const year = parseInt(this.startDate) || new Date().getFullYear()
const monthIndex = parseInt(key.replace('月', '')) - 1
const daysInMonth = new Date(year, monthIndex + 1, 0).getDate()
totalMinutes = daysInMonth * 1440
} else {
totalMinutes = 1440
}
const rate = this.calculateWorkRate(data.duration, totalMinutes)
rateData.push(Number(rate))
})
console.log('趋势图数据构建成功:', { categories, durationData, rateData, isYearView })
this.trendChartData = {
categories: categories,
series: [
{
name: "停机时间",
type: "column",
index: 0,
data: durationData
},
{
name: "作业率",
type: "line",
index: 1,
style: "curve",
data: rateData
}
]
}
},
// 格式化日期(简短格式)
formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
const month = date.getMonth() + 1
const day = date.getDate()
return `${month}/${day}`
},
// 格式化日期为月份(用于年视图)
formatDateByMonth(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
const month = date.getMonth() + 1
return `${month}`
},
// 秒转分钟(保留整数)
secondsToMinutes(seconds) {
if (!seconds || seconds === 0) return 0
return Math.round(Number(seconds) / 60)
},
// 格式化为完整日期(用于查询)
formatFullDate(dateStr, isStart) {
if (!dateStr) return ''
// 如果已经是完整日期格式 yyyy-MM-dd直接返回
if (dateStr.length === 10) {
return dateStr
}
// 月份格式 yyyy-MM
if (dateStr.length === 7) {
if (isStart) {
return `${dateStr}-01`
} else {
const [year, month] = dateStr.split('-')
const lastDay = new Date(year, month, 0).getDate()
return `${dateStr}-${String(lastDay).padStart(2, '0')}`
}
}
// 年份格式 yyyy
if (dateStr.length === 4) {
if (isStart) {
return `${dateStr}-01-01`
} else {
return `${dateStr}-12-31`
}
}
return dateStr
}
},
// 生命周期钩子
mounted() {
this.loadStoppageData()
}
};
</script>
<style scoped lang="scss">
/* 页面容器 */
.page-container {
background: #f5f7fa;
padding: 24rpx;
}
/* 时间维度切换 */
.time-tab-bar {
display: flex;
background: #fff;
border-radius: 8rpx;
padding: 8rpx;
margin-bottom: 24rpx;
border: 1rpx solid #e4e7ed;
}
.time-tab-item {
flex: 1;
text-align: center;
padding: 16rpx 0;
font-size: 26rpx;
color: #606266;
border-radius: 6rpx;
transition: all 0.2s;
}
.time-tab-active {
background: #0066cc;
color: #fff;
font-weight: 500;
}
/* 日期选择区 */
.date-selector {
background: #fff;
border-radius: 8rpx;
padding: 24rpx;
margin-bottom: 24rpx;
border: 1rpx solid #e4e7ed;
}
.date-range-group {
display: flex;
justify-content: space-between;
}
.date-input {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 32rpx;
background: #f5f7fa;
border: 1rpx solid #e4e7ed;
&:first-child {
border-radius: 6rpx 0 0 6rpx;
border-right: none;
}
&:last-child {
border-radius: 0 6rpx 6rpx 0;
border-left: none;
}
}
.date-label {
font-size: 26rpx;
color: #909399;
margin-right: 16rpx;
}
.date-value {
font-size: 28rpx;
color: #303133;
font-weight: 500;
flex: 1;
text-align: right;
}
.date-separator {
font-size: 28rpx;
color: #fff;
background: #0066cc;
padding: 24rpx 20rpx;
display: flex;
align-items: center;
justify-content: center;
min-width: 80rpx;
}
/* 区块样式 */
.summary-section,
.chart-section,
.detail-section {
margin-bottom: 24rpx;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
padding-left: 16rpx;
border-left: 4rpx solid #0066cc;
}
.section-title {
font-size: 30rpx;
font-weight: 500;
color: #303133;
}
.section-date {
font-size: 24rpx;
color: #909399;
}
/* 汇总卡片网格 */
.summary-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
}
.summary-card {
background: #fff;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
padding: 28rpx 20rpx;
text-align: center;
}
.summary-label {
display: block;
font-size: 24rpx;
color: #909399;
margin-bottom: 16rpx;
}
.summary-value-box {
display: flex;
align-items: baseline;
justify-content: center;
}
.summary-value {
font-size: 40rpx;
font-weight: 600;
color: #0066cc;
line-height: 1;
}
.summary-unit {
font-size: 22rpx;
color: #909399;
margin-left: 6rpx;
}
/* 图表容器 */
.chart-wrapper {
background: #fff;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
padding: 24rpx 16rpx;
min-height: 450rpx;
}
/* 趋势图固定高度 */
.trend-chart {
height: 500rpx;
min-height: 500rpx;
}
/* 单个饼图容器 */
.pie-chart-single {
background: #fff;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
padding: 32rpx 0;
min-height: 480rpx;
}
/* 空状态图表占位 */
.empty-chart {
background: #fff;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
padding: 100rpx 0;
min-height: 480rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20rpx;
}
.empty-icon {
font-size: 80rpx;
opacity: 0.3;
}
.empty-text {
font-size: 28rpx;
color: #909399;
}
/* 停机详情列表 */
.detail-list {
background: #fff;
border-radius: 8rpx;
border: 1rpx solid #e4e7ed;
overflow: hidden;
}
.detail-item {
padding: 24rpx;
border-bottom: 1rpx solid #f5f7fa;
&:last-child {
border-bottom: none;
}
}
.detail-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.detail-time {
font-size: 26rpx;
color: #606266;
}
.detail-duration {
font-size: 28rpx;
color: #0066cc;
font-weight: 600;
}
.detail-info {
display: flex;
align-items: baseline;
margin-bottom: 8rpx;
&:last-child {
margin-bottom: 0;
}
}
.detail-label {
font-size: 24rpx;
color: #909399;
min-width: 120rpx;
}
.detail-text {
font-size: 26rpx;
color: #303133;
flex: 1;
}
/* 空状态 */
.empty-state {
padding: 100rpx 0;
text-align: center;
}
.empty-text {
font-size: 28rpx;
color: #909399;
}
</style>