超市会议

This commit is contained in:
zjf committed 2025-10-09 18:38:18 +08:00
1 parent 30e3bf6655
commit 0ebd6737fe
16 files changed
+1397 -162

No files matched your search

@@ -46,7 +46,7 @@
</el-table-column>
<el-table-column prop="createBy" label="申请人" show-overflow-tooltip/>
<el-table-column prop="assigneeId" label="会议负责人" show-overflow-tooltip/>
<el-table-column prop="bookingDate" label="开始时间" show-overflow-tooltip/>
<el-table-column prop="bookingDate" label="预约日期" show-overflow-tooltip/>
<el-table-column prop="startTime" label="开始时间" show-overflow-tooltip/>
<el-table-column prop="endTime" label="结束时间" show-overflow-tooltip/>
<el-table-column label="操作" width="150">
@@ -0,0 +1,337 @@
<template>
<el-dialog
:model-value="visible"
@update:model-value="(val) => emit('update:visible', val)"
title="会议室预约详情"
width="80%"
:before-close="handleClose"
>
<div class="booking-detail-container">
<!-- 查询条件 -->
<el-form :model="queryForm" :inline="true" class="mb20">
<el-form-item label="会议室">
<el-input :value="roomName" disabled style="width: 200px;" />
</el-form-item>
<el-form-item label="查询日期">
<el-date-picker
v-model="queryForm.bookingDate"
type="date"
placeholder="请选择查询日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 200px"
@change="loadBookingData"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadBookingData">查询</el-button>
</el-form-item>
</el-form>
<!-- 时间轴显示 -->
<div class="time-schedule">
<h3>{{ queryForm.bookingDate || '请选择日期' }} 的预约情况</h3>
<!-- 时间轴 -->
<div class="time-axis">
<div class="time-slots">
<div
v-for="timeSlot in timeSlots"
:key="timeSlot.startTime"
class="time-slot"
:class="{
'booked': timeSlot.status === 'booked',
'reviewing': timeSlot.status === 'reviewing',
'available': timeSlot.status === 'available',
'current': timeSlot.isCurrent
}"
@click="handleTimeSlotClick(timeSlot)"
>
<div class="time-label">{{ timeSlot.startTime }} - {{ timeSlot.endTime }}</div>
<div v-if="timeSlot.bookingInfo" class="booking-info">
<div class="booking-title">{{ timeSlot.bookingInfo.title }}</div>
<div class="booking-user">{{ timeSlot.bookingInfo.applicantName }}</div>
<div class="booking-status">
<el-tag :type="timeSlot.status === 'booked' ? 'success' : timeSlot.status === 'reviewing' ? 'warning' : 'info'">
{{ timeSlot.statusText }}
</el-tag>
</div>
</div>
<div v-else class="available">
{{ timeSlot.statusText }}
</div>
</div>
</div>
</div>
<!-- 预约列表 -->
<div class="booking-list" v-if="bookingList.length > 0">
<h4>当日预约列表</h4>
<el-table :data="bookingList" border>
<el-table-column prop="title" label="会议主题" show-overflow-tooltip />
<el-table-column prop="applicantName" label="申请人" show-overflow-tooltip />
<el-table-column prop="startTime" label="开始时间" show-overflow-tooltip />
<el-table-column prop="endTime" label="结束时间" show-overflow-tooltip />
<el-table-column prop="status" label="状态" show-overflow-tooltip>
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="description" label="会议说明" show-overflow-tooltip />
</el-table>
</div>
<!-- 无预约提示 -->
<div v-else-if="queryForm.bookingDate" class="no-booking">
<el-empty description="当日暂无预约" />
</div>
</div>
</div>
<template #footer>
<el-button @click="() => emit('update:visible', false)">关闭</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { getBookingDetailPage } from "/src/api/integrated/meetingRoom";
// Props
interface Props {
visible: boolean;
roomId: string;
roomName: string;
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
roomId: '',
roomName: ''
});
// Emits
const emit = defineEmits<{
'update:visible': [value: boolean];
}>();
// 响应式数据
const queryForm = reactive({
bookingDate: '',
roomId: ''
});
const bookingList = ref([]);
const loading = ref(false);
// 时间槽位数据 - 直接使用API返回的数据
const timeSlots = ref([]);
// 状态类型映射
const getStatusType = (status: string) => {
const statusMap = {
'1': 'warning', // 审核中
'2': 'success', // 审核通过
'3': 'danger', // 审核拒绝
'4': 'info' // 已取消
};
return statusMap[status] || 'info';
};
// 状态文本映射
const getStatusText = (status: string) => {
const statusMap = {
'1': '审核中',
'2': '审核通过',
'3': '审核拒绝',
'4': '已取消'
};
return statusMap[status] || '未知状态';
};
// 加载预约数据
const loadBookingData = async () => {
if (!queryForm.bookingDate) {
ElMessage.warning('请选择查询日期');
return;
}
try {
loading.value = true;
// 调用新的API接口获取预约详情页面数据
const response = await getBookingDetailPage(queryForm.roomId, queryForm.bookingDate);
// 更新时间段数据 - API返回的数据在data字段中
timeSlots.value = response.data?.timeSlots || [];
// 更新预约列表数据
bookingList.value = response.data?.dailyBookings || [];
// 更新房间名称(如果API返回了的话)
if (response.data?.roomName) {
// 可以通过emit通知父组件更新房间名称,或者直接更新props
// emit('update:roomName', response.data.roomName);
}
ElMessage.success('数据加载成功');
} catch (error) {
console.error('加载预约数据失败:', error);
ElMessage.error('加载数据失败');
// 如果API调用失败,使用模拟数据作为备选
const mockData = [
{
id: '1',
title: '项目讨论会',
applicantName: '张三',
startTime: '09:00',
endTime: '11:00',
status: '2',
statusText: '审核通过',
description: '讨论项目进度'
},
{
id: '2',
title: '技术评审',
applicantName: '李四',
startTime: '14:00',
endTime: '16:00',
status: '1',
statusText: '审核中',
description: '技术方案评审'
}
];
bookingList.value = mockData;
} finally {
loading.value = false;
}
};
// 时间槽点击事件
const handleTimeSlotClick = (timeSlot: any) => {
if (timeSlot.status === 'booked') {
ElMessage.info(`该时间段已被预约: ${timeSlot.bookingInfo?.title || '未知会议'}`);
} else if (timeSlot.status === 'reviewing') {
ElMessage.info(`该时间段正在审核中: ${timeSlot.bookingInfo?.title || '未知会议'}`);
} else {
ElMessage.info(`该时间段可预约: ${timeSlot.startTime} - ${timeSlot.endTime}`);
}
};
// 关闭对话框
const handleClose = () => {
emit('update:visible', false);
};
// 监听visible变化,初始化数据
watch(() => props.visible, (newVal) => {
if (newVal) {
queryForm.roomId = props.roomId;
queryForm.bookingDate = new Date().toISOString().split('T')[0]; // 默认今天
loadBookingData();
}
});
</script>
<style scoped>
.booking-detail-container {
padding: 20px;
}
.time-schedule {
margin-top: 20px;
}
.time-axis {
margin: 20px 0;
}
.time-slots {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 8px;
}
.time-slot {
border: 1px solid #e4e7ed;
border-radius: 8px;
padding: 15px;
cursor: pointer;
transition: all 0.3s;
min-height: 80px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.time-slot:hover {
border-color: #409eff;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}
.time-slot.booked {
background-color: #f0f9ff;
border-color: #409eff;
}
.time-slot.reviewing {
background-color: #fefce8;
border-color: #eab308;
}
.time-slot.available {
background-color: #f0fdf4;
border-color: #22c55e;
}
.time-slot.current {
background-color: #fff7e6;
border-color: #ff9500;
}
.time-label {
font-weight: bold;
color: #606266;
margin-bottom: 8px;
}
.booking-info {
text-align: center;
}
.booking-title {
font-weight: bold;
color: #303133;
margin-bottom: 4px;
}
.booking-user {
color: #606266;
font-size: 12px;
margin-bottom: 4px;
}
.available {
color: #67c23a;
font-weight: bold;
}
.booking-list {
margin-top: 30px;
}
.no-booking {
text-align: center;
padding: 40px;
}
.mb20 {
margin-bottom: 20px;
}
</style>
@@ -37,8 +37,11 @@
<!-- <el-table-column prop="availableTime" label="可用时间段" show-overflow-tooltip/>-->
<!-- <el-table-column prop="cleanUserId" label="保洁人员" show-overflow-tooltip/>-->
<!-- <el-table-column prop="serviceUserId" label="会务人员" show-overflow-tooltip/>-->
<el-table-column label="操作" width="150">
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button icon="view" text type="primary" @click="viewBookingDetail(scope.row)">
预约详情
</el-button>
<el-button icon="edit-pen" text type="primary" v-auth="'integrated_meetingRoom_edit'"
@click="formDialogRef.openDialog(scope.row.id)">编辑</el-button>
<el-button icon="delete" text type="primary" v-auth="'integrated_meetingRoom_del'" @click="handleDelete([scope.row.id])">删除</el-button>
@@ -51,6 +54,13 @@
<!-- 编辑、新增 -->
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
<!-- 预约详情 -->
<BookingDetail
v-model:visible="bookingDetailVisible"
:room-id="selectedRoomId"
:room-name="selectedRoomName"
/>
</div>
</template>
@@ -62,11 +72,17 @@ import { useDict } from '/src/hooks/dict';
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const BookingDetail = defineAsyncComponent(() => import('./bookingDetail.vue'));
// 定义查询字典
const { meeting_status } = useDict('meeting_status')
// 定义变量内容
const formDialogRef = ref()
// 预约详情相关
const bookingDetailVisible = ref(false);
const selectedRoomId = ref('');
const selectedRoomName = ref('');
// 搜索变量
const queryRef = ref()
const showSearch = ref(true)
@@ -109,6 +125,13 @@ const selectionChangHandle = (objs: { id: string }[]) => {
multiple.value = !objs.length;
};
// 查看预约详情
const viewBookingDetail = (row: any) => {
selectedRoomId.value = row.id;
selectedRoomName.value = row.roomName;
bookingDetailVisible.value = true;
};
// 删除操作
const handleDelete = async (ids: string[]) => {
try {
@@ -0,0 +1,575 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 查询表单 -->
<el-row v-show="showSearch">
<el-form :model="queryForm" ref="queryRef" :inline="true" @keyup.enter="loadData">
<el-form-item label="开始日期" prop="startDate">
<el-date-picker
v-model="queryForm.startDate"
type="date"
placeholder="请选择开始日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 160px"
/>
</el-form-item>
<el-form-item label="结束日期" prop="endDate">
<el-date-picker
v-model="queryForm.endDate"
type="date"
placeholder="请选择结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 160px"
/>
</el-form-item>
<!-- <el-form-item label="统计维度" prop="dimension">
<el-select v-model="queryForm.dimension" placeholder="请选择统计维度" style="width: 140px">
<el-option label="部门统计" value="dept" />
<el-option label="负责人统计" value="assignee" />
<el-option label="时间统计" value="time" />
</el-select>
</el-form-item> -->
<!-- <el-form-item label="时间粒度" prop="timeGranularity">
<el-select v-model="queryForm.timeGranularity" placeholder="请选择时间粒度" style="width: 120px">
<el-option label="按天" value="day" />
<el-option label="按周" value="week" />
<el-option label="按月" value="month" />
</el-select>
</el-form-item> -->
<el-form-item>
<el-button icon="search" type="primary" @click="loadData" :loading="loading">
查询
</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-row>
<!-- 操作按钮 -->
<el-row>
<div class="mb8" style="width: 100%">
<right-toolbar v-model:showSearch="showSearch" class="ml10 mr20" style="float: right;" />
</div>
</el-row>
<!-- 总体统计卡片 -->
<el-row :gutter="20" class="mb20">
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-value">{{ statsData.overallStats?.totalBookings || 0 }}</div>
<div class="stat-label">总预约数</div>
</div>
<div class="stat-icon">
<el-icon><Calendar /></el-icon>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-value">{{ statsData.overallStats?.approvedBookings || 0 }}</div>
<div class="stat-label">审核通过</div>
</div>
<div class="stat-icon">
<el-icon><Check /></el-icon>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-value">{{ (statsData.overallStats?.approvalRate || 0).toFixed(1) }}%</div>
<div class="stat-label">通过率</div>
</div>
<div class="stat-icon">
<el-icon><TrendCharts /></el-icon>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-value">{{ getAvgDuration() }}</div>
<div class="stat-label">平均时长(分钟)</div>
</div>
<div class="stat-icon">
<el-icon><Timer /></el-icon>
</div>
</el-card>
</el-col>
</el-row>
<!-- 状态分布卡片 -->
<el-row :gutter="20" class="mb20">
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-value">{{ statsData.overallStats?.pendingBookings || 0 }}</div>
<div class="stat-label">审核中</div>
</div>
<div class="stat-icon">
<el-icon><Clock /></el-icon>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-value">{{ statsData.overallStats?.rejectedBookings || 0 }}</div>
<div class="stat-label">审核拒绝</div>
</div>
<div class="stat-icon">
<el-icon><Close /></el-icon>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-value">{{ statsData.overallStats?.cancelledBookings || 0 }}</div>
<div class="stat-label">已取消</div>
</div>
<div class="stat-icon">
<el-icon><CircleClose /></el-icon>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-value">{{ Math.round(parseInt(statsData.overallStats?.totalDuration || '0') / 60) }}</div>
<div class="stat-label">总时长(小时)</div>
</div>
<div class="stat-icon">
<el-icon><Clock /></el-icon>
</div>
</el-card>
</el-col>
</el-row>
<!-- 图表展示 -->
<el-row :gutter="20" class="mb20">
<el-col :span="12">
<el-card>
<template #header>
<div class="card-header">
<span>状态分布</span>
</div>
</template>
<div ref="statusChartRef" style="height: 300px;"></div>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<template #header>
<div class="card-header">
<span>时间趋势</span>
</div>
</template>
<div ref="timeChartRef" style="height: 300px;"></div>
</el-card>
</el-col>
</el-row>
<!-- 数据表格 - 使用普通div替代el-card -->
<div style="background: white; border: 1px solid #dcdfe6; border-radius: 4px; min-height: 400px; width: 100%; overflow-x: auto;">
<!-- 卡片头部 -->
<div style="padding: 18px 20px; border-bottom: 1px solid #ebeef5; background: #f5f7fa; width: 100%;">
<div class="card-header">
<span>详细统计</span>
<el-radio-group v-model="currentTab" @change="handleTabChange">
<el-radio-button label="dept">部门统计</el-radio-button>
<!-- <el-radio-button label="assignee">负责人统计</el-radio-button> -->
<el-radio-button label="time">时间统计</el-radio-button>
<el-radio-button label="room">会议室统计</el-radio-button>
</el-radio-group>
</div>
</div>
<!-- 卡片内容区域 -->
<div style="padding: 20px; background: white; width: 100%; overflow-x: auto;">
<!-- 部门统计表格 -->
<div v-show="currentTab === 'dept'">
<el-table :data="statsData.deptStats" v-loading="loading" border>
<el-table-column prop="deptName" label="部门名称" show-overflow-tooltip />
<el-table-column prop="bookingCount" label="预约数量" show-overflow-tooltip />
<el-table-column prop="approvedCount" label="通过数量" show-overflow-tooltip />
<el-table-column prop="approvalRate" label="通过率" show-overflow-tooltip>
<template #default="scope">
{{ formatPercentage(scope.row.approvalRate) }}%
</template>
</el-table-column>
<el-table-column prop="totalDuration" label="总时长(分钟)" show-overflow-tooltip />
</el-table>
</div>
<!-- 负责人统计表格 -->
<!-- <div v-show="currentTab === 'assignee'">
<el-table :data="statsData.assigneeStats" v-loading="loading" border>
<el-table-column prop="assigneeName" label="负责人姓名" show-overflow-tooltip />
<el-table-column prop="bookingCount" label="预约数量" show-overflow-tooltip />
<el-table-column prop="approvedCount" label="通过数量" show-overflow-tooltip />
<el-table-column prop="approvalRate" label="通过率" show-overflow-tooltip>
<template #default="scope">
{{ typeof scope.row.approvalRate === 'number' ? scope.row.approvalRate.toFixed(1) : parseFloat(scope.row.approvalRate || 0).toFixed(1) }}%
</template>
</el-table-column>
<el-table-column prop="totalDuration" label="总时长(分钟)" show-overflow-tooltip />
</el-table>
</div> -->
<!-- 时间统计表格 -->
<div v-show="currentTab === 'time'">
<el-table :data="statsData.timeStats" v-loading="loading" border>
<el-table-column prop="timeLabel" label="时间" show-overflow-tooltip />
<el-table-column prop="bookingCount" label="预约数量" show-overflow-tooltip />
<el-table-column prop="approvedCount" label="通过数量" show-overflow-tooltip />
<el-table-column prop="totalDuration" label="总时长(分钟)" show-overflow-tooltip />
</el-table>
</div>
<!-- 会议室统计表格 -->
<div v-show="currentTab === 'room'">
<el-table :data="statsData.roomStats" v-loading="loading" border>
<el-table-column prop="roomName" label="会议室名称" show-overflow-tooltip />
<el-table-column prop="bookingCount" label="预约数量" show-overflow-tooltip />
<el-table-column prop="approvedCount" label="通过数量" show-overflow-tooltip />
<el-table-column prop="usageRate" label="使用率" show-overflow-tooltip>
<template #default="scope">
{{ formatPercentage(scope.row.usageRate) }}%
</template>
</el-table-column>
<el-table-column prop="totalDuration" label="总时长(分钟)" show-overflow-tooltip />
</el-table>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="MeetingSummary">
import { ref, reactive, onMounted, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import { Calendar, Check, TrendCharts, Timer, Clock, Close, CircleClose } from '@element-plus/icons-vue';
import * as echarts from 'echarts';
import { getMeetingStats, type MeetingStatsQuery, type MeetingStatsData } from '/@/api/integrated/meetingStats';
// 响应式数据
const loading = ref(false);
const showSearch = ref(true);
const queryRef = ref();
const currentTab = ref('dept');
// 查询表单
const queryForm = reactive<MeetingStatsQuery>({
startDate: '',
endDate: '',
dimension: 'dept',
timeGranularity: 'day'
});
// 统计数据
const statsData = ref<MeetingStatsData>({
overallStats: {
totalBookings: 0,
approvedBookings: 0,
pendingBookings: 0,
rejectedBookings: 0,
cancelledBookings: 0,
approvalRate: 0,
avgDuration: 0,
totalDuration: 0
},
deptStats: [],
assigneeStats: [],
timeStats: [],
statusStats: [],
roomStats: []
});
// 表格刷新key
const tableKey = ref(0);
// 图表引用
const statusChartRef = ref();
const timeChartRef = ref();
let statusChart: echarts.ECharts | null = null;
let timeChart: echarts.ECharts | null = null;
// 加载数据
const loadData = async () => {
try {
loading.value = true;
const response = await getMeetingStats(queryForm);
if (response.code === 0) {
// 直接使用API返回的数据,不做复杂处理
statsData.value = response.data;
// 强制刷新表格
tableKey.value++;
// 强制触发响应式更新
await nextTick();
initCharts();
} else {
ElMessage.error(response.msg || '获取统计数据失败');
}
} catch (error: any) {
console.error('加载统计数据失败:', error);
ElMessage.error(error.message || '加载统计数据失败');
} finally {
loading.value = false;
}
};
// 重置查询
const resetQuery = () => {
queryRef.value?.resetFields();
queryForm.startDate = '';
queryForm.endDate = '';
queryForm.dimension = 'dept';
queryForm.timeGranularity = 'day';
loadData();
};
// 切换标签页
const handleTabChange = (value: string) => {
currentTab.value = value;
};
// 格式化百分比
const formatPercentage = (value: any) => {
if (value === null || value === undefined || value === '') {
return '0.0';
}
if (typeof value === 'number') {
return value.toFixed(1);
}
if (typeof value === 'string') {
const num = parseFloat(value);
return isNaN(num) ? '0.0' : num.toFixed(1);
}
return '0.0';
};
// 获取平均时长
const getAvgDuration = () => {
const avgDuration = statsData.value.overallStats?.avgDuration;
// 处理 null 或 undefined 的情况
if (!avgDuration) {
return 0;
}
// 处理对象类型
if (typeof avgDuration === 'object' && avgDuration !== null && avgDuration.parsedValue) {
return Math.round(avgDuration.parsedValue);
}
// 处理字符串类型
if (typeof avgDuration === 'string') {
return Math.round(parseFloat(avgDuration) || 0);
}
// 处理数字类型
if (typeof avgDuration === 'number') {
return Math.round(avgDuration);
}
// 默认返回0
return 0;
};
// 初始化图表
const initCharts = () => {
initStatusChart();
initTimeChart();
};
// 初始化状态分布饼图
const initStatusChart = () => {
if (!statusChartRef.value) return;
if (statusChart) {
statusChart.dispose();
}
statusChart = echarts.init(statusChartRef.value);
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '预约状态',
type: 'pie',
radius: '50%',
data: statsData.value.statusStats.map(item => ({
value: parseInt(item.count || 0) || 0,
name: item.statusName || '未知状态'
})),
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
statusChart.setOption(option);
};
// 初始化时间趋势折线图
const initTimeChart = () => {
if (!timeChartRef.value) return;
if (timeChart) {
timeChart.dispose();
}
timeChart = echarts.init(timeChartRef.value);
const timeData = statsData.value.timeStats || [];
const dates = timeData.map(item => item.timeLabel || '未知日期');
const bookingCounts = timeData.map(item => parseInt(item.bookingCount || 0) || 0);
const approvedCounts = timeData.map(item => parseInt(item.approvedCount || 0) || 0);
const option = {
tooltip: {
trigger: 'axis'
},
legend: {
data: ['预约数量', '通过数量']
},
xAxis: {
type: 'category',
data: dates
},
yAxis: {
type: 'value'
},
series: [
{
name: '预约数量',
type: 'line',
data: bookingCounts,
smooth: true
},
{
name: '通过数量',
type: 'line',
data: approvedCounts,
smooth: true
}
]
};
timeChart.setOption(option);
};
// 组件挂载时加载数据
onMounted(() => {
loadData();
});
</script>
<style scoped>
.stat-card {
position: relative;
overflow: hidden;
}
.stat-card :deep(.el-card__body) {
padding: 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.stat-content {
flex: 1;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #409eff;
line-height: 1;
margin-bottom: 8px;
}
.stat-label {
font-size: 14px;
color: #666;
}
.stat-icon {
font-size: 40px;
color: #409eff;
opacity: 0.3;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.mb20 {
margin-bottom: 20px;
}
.mb8 {
margin-bottom: 8px;
}
.ml10 {
margin-left: 10px;
}
.mr20 {
margin-right: 20px;
}
/* 确保页面可以滚动 */
.layout-padding {
overflow-x: auto !important;
overflow-y: auto !important;
max-width: 100% !important;
width: 100% !important;
}
.layout-padding-auto {
overflow-x: auto !important;
overflow-y: auto !important;
max-width: 100% !important;
width: 100% !important;
}
.layout-padding-view {
overflow-x: auto !important;
overflow-y: auto !important;
max-width: 100% !important;
width: 100% !important;
}
</style>