416 lines
11 KiB
Vue
416 lines
11 KiB
Vue
<script setup lang="ts">
|
||
import { ref, computed, onMounted, h } from 'vue';
|
||
import { Page } from '@vben/common-ui';
|
||
import dayjs from 'dayjs';
|
||
import type { TableColumnType } from 'ant-design-vue';
|
||
import { Radio, Select, Button, Table } from 'ant-design-vue';
|
||
import { getMeetName } from '#/api/property/roomBooking';
|
||
import { getAppointmentListByDate,getAppointmentListBymeetId } from '#/api/property/roomBooking/conferenceView';
|
||
import type { RadioChangeEvent } from 'ant-design-vue';
|
||
import type { SelectValue } from 'ant-design-vue/es/select';
|
||
|
||
// 会议室和预约信息的接口类型
|
||
interface ConferenceRoom {
|
||
id: string;
|
||
name: string;
|
||
}
|
||
interface ConferenceBooking {
|
||
id: string | number;
|
||
tbConferenceId: string | number; // 会议室ID
|
||
bookingName: string; // 预约人
|
||
deptName: string; // 部门名称
|
||
subject: string; // 会议主题
|
||
startTime: string; // 开始时间
|
||
endTime: string; // 结束时间
|
||
bookingDate: string; // 预约日期
|
||
status: number; // 状态
|
||
}
|
||
|
||
// 视图模式
|
||
const viewMode = ref<'date' | 'room'>('date');
|
||
|
||
// 会议室列表
|
||
const roomList = ref<ConferenceRoom[]>([]);
|
||
|
||
// 选中的会议室
|
||
const selectedRoom = ref<string>('');
|
||
|
||
// 选中的日期
|
||
const selectedDate = ref<string>('');
|
||
|
||
// 一周的日期
|
||
const weekDates = ref<string[]>([]);
|
||
|
||
// 预约数据
|
||
const bookings = ref<any[]>([]);
|
||
// 新增:表格渲染用的结构
|
||
const bookingTable = ref<Record<string, Record<string, any>>>({});
|
||
// 新增:加载状态
|
||
const loading = ref(false);
|
||
|
||
// 时间段只显示"上午" "下午"
|
||
const timeSlots = ['上午', '下午'];
|
||
|
||
// 生成一周日期
|
||
function generateWeekDates(): void {
|
||
const today = dayjs();
|
||
// 获取本周的周一
|
||
const startOfWeek = today.startOf('week');
|
||
const dates = Array.from({ length: 7 }, (_, i) => {
|
||
return startOfWeek.add(i, 'day').format('YYYY-MM-DD');
|
||
});
|
||
weekDates.value = dates;
|
||
selectedDate.value = dates[0] ?? '';
|
||
}
|
||
|
||
// 获取预约数据
|
||
async function fetchBookings(): Promise<void> {
|
||
loading.value = true;
|
||
try {
|
||
if (viewMode.value === 'date') {
|
||
const roomRes = await getMeetName();
|
||
roomList.value = ([roomRes]).map((item: any) => ({
|
||
id: item.value,
|
||
name: item.name
|
||
}));
|
||
const appointmentRes = await getAppointmentListByDate({
|
||
appointmentDate: selectedDate.value
|
||
});
|
||
bookings.value = (appointmentRes || []).map((item: any) => ({
|
||
id: item.id,
|
||
meetId: item.meetId,
|
||
name: item.name,
|
||
person: item.person,
|
||
scheduledEndtime: item.scheduledEndtime,
|
||
scheduledStarttime: item.scheduledStarttime,
|
||
unit: item.unit,
|
||
personName: item.personName,
|
||
slots: item.slots,
|
||
unitName: item.unitName,
|
||
subject: item.subject,
|
||
deptName: item.unitName,
|
||
bookingName: item.personName,
|
||
startTime: item.scheduledStarttime,
|
||
endTime: item.scheduledEndtime,
|
||
bookingDate: item.scheduledStarttime,
|
||
tbConferenceId: item.meetId,
|
||
// 兼容后续渲染
|
||
}));
|
||
// 处理为表格结构
|
||
const table: Record<string, Record<string, any>> = { '上午': {}, '下午': {} };
|
||
roomList.value.forEach(room => {
|
||
['上午', '下午'].forEach(slot => {
|
||
const booking = bookings.value.find(b => b.name === room.name && b.slots === slot);
|
||
table[slot][room.name] = booking || null;
|
||
});
|
||
});
|
||
bookingTable.value = table;
|
||
} else {
|
||
const res = await getAppointmentListBymeetId({
|
||
meetId: selectedRoom.value
|
||
});
|
||
bookings.value = (res || []).map((item: any) => ({
|
||
id: item.id,
|
||
meetId: item.meetId,
|
||
name: item.name,
|
||
person: item.person,
|
||
scheduledEndtime: item.scheduledEndtime,
|
||
scheduledStarttime: item.scheduledStarttime,
|
||
unit: item.unit,
|
||
personName: item.personName,
|
||
slots: item.slots,
|
||
unitName: item.unitName,
|
||
subject: item.subject,
|
||
deptName: item.unitName,
|
||
bookingName: item.personName,
|
||
startTime: item.scheduledStarttime,
|
||
endTime: item.scheduledEndtime,
|
||
bookingDate: item.scheduledStarttime,
|
||
tbConferenceId: item.meetId,
|
||
}));
|
||
const table: Record<string, Record<string, any>> = { '上午': {}, '下午': {} };
|
||
weekDates.value.forEach(date => {
|
||
['上午', '下午'].forEach(slot => {
|
||
const booking = bookings.value.find(b => dayjs(b.scheduledStarttime).format('YYYY-MM-DD') === date && b.slots === slot);
|
||
table[slot][date] = booking || null;
|
||
});
|
||
});
|
||
bookingTable.value = table;
|
||
}
|
||
console.log(bookingTable.value,'bookingTable.value');
|
||
|
||
} catch (error) {
|
||
console.error('获取预约数据失败:', error);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
// 切换视图模式
|
||
function handleViewModeChange(e: RadioChangeEvent): void {
|
||
viewMode.value = e.target.value;
|
||
if (viewMode.value === 'date') {
|
||
selectedDate.value = weekDates.value[0] ?? '';
|
||
} else {
|
||
selectedRoom.value = roomList.value[0]?.id ?? '';
|
||
}
|
||
fetchBookings();
|
||
}
|
||
|
||
// 切换日期
|
||
function handleDateChange(date: string): void {
|
||
selectedDate.value = date;
|
||
fetchBookings();
|
||
}
|
||
|
||
// 切换会议室
|
||
function handleRoomChange(value: SelectValue): void {
|
||
selectedRoom.value = value as string;
|
||
fetchBookings();
|
||
}
|
||
|
||
// 获取单元格预约信息(上午/下午)
|
||
function getCellBooking(col: string, time: string): ConferenceBooking | undefined {
|
||
// 判断时间属于上午还是下午
|
||
function isMorning(startTime: string) {
|
||
const hour = dayjs(startTime).hour();
|
||
return hour < 12;
|
||
}
|
||
if (viewMode.value === 'date') {
|
||
const room = roomList.value.find(r => r.name === col);
|
||
return bookings.value.find(b =>
|
||
b.tbConferenceId === room?.id &&
|
||
((time === '上午' && isMorning(b.startTime)) || (time === '下午' && !isMorning(b.startTime)))
|
||
);
|
||
} else {
|
||
return bookings.value.find(b =>
|
||
dayjs(b.bookingDate).format('YYYY-MM-DD') === col &&
|
||
((time === '上午' && isMorning(b.startTime)) || (time === '下午' && !isMorning(b.startTime)))
|
||
);
|
||
}
|
||
}
|
||
|
||
// 修改渲染预约单元格的函数
|
||
function renderBookingCell(booking: ConferenceBooking) {
|
||
return h('div',
|
||
{
|
||
class: 'booking-cell'
|
||
},
|
||
[
|
||
h('span',
|
||
{ class: 'booking-user' },
|
||
`预约人:${booking.bookingName}`
|
||
),
|
||
h('span',
|
||
{ class: 'booking-dept' },
|
||
`单位:${booking.deptName}`
|
||
),
|
||
h('span',
|
||
{ class: 'booking-subject' },
|
||
`主题:${booking.subject}`
|
||
)
|
||
]
|
||
);
|
||
}
|
||
|
||
// 随机浅色背景色生成函数
|
||
function getRandomBgColor() {
|
||
const r = Math.floor(200 + Math.random() * 55);
|
||
const g = Math.floor(200 + Math.random() * 55);
|
||
const b = Math.floor(200 + Math.random() * 55);
|
||
return {
|
||
background: `rgba(${r},${g},${b},0.5)`,
|
||
borderRadius: '6px',
|
||
padding: '60px 10px',
|
||
transition: 'background 0.3s'
|
||
};
|
||
}
|
||
|
||
// 表格列配置
|
||
interface TableRecord {
|
||
time: string;
|
||
key: string;
|
||
[key: string]: any;
|
||
}
|
||
|
||
// 会议室列补全到7个
|
||
function getFullRoomList() {
|
||
const rooms = roomList.value.slice();
|
||
const count = rooms.length;
|
||
if (viewMode.value === 'date' && count < 7) {
|
||
for (let i = count; i < 7; i++) {
|
||
rooms.push({ id: `empty_${i}`, name: '' });
|
||
}
|
||
}
|
||
return rooms;
|
||
}
|
||
|
||
const columns = computed<TableColumnType<TableRecord>[]>(() => {
|
||
const baseColumns: TableColumnType<TableRecord>[] = [{
|
||
title: '时间',
|
||
dataIndex: 'slot',
|
||
key: 'slot',
|
||
width: 100,
|
||
fixed: 'left' as const
|
||
}];
|
||
const dynamicColumns: TableColumnType<TableRecord>[] = viewMode.value === 'date'
|
||
? getFullRoomList().map(room => ({
|
||
title: room.name || ' ',
|
||
dataIndex: room.name,
|
||
key: room.name,
|
||
width: 200,
|
||
}))
|
||
: weekDates.value.map(date => ({
|
||
title: dayjs(date).format('YYYY-MM-DD'),
|
||
dataIndex: date,
|
||
key: date,
|
||
width: 200,
|
||
}));
|
||
return [...baseColumns, ...dynamicColumns];
|
||
});
|
||
|
||
const tableData = computed<TableRecord[]>(() => {
|
||
const slots = ['上午', '下午'];
|
||
return slots.map(slot => {
|
||
const row: any = { slot };
|
||
const cols = viewMode.value === 'date'
|
||
? getFullRoomList().map(room => room.name)
|
||
: weekDates.value;
|
||
cols.forEach(col => {
|
||
row[col] = bookingTable.value[slot]?.[col] || null;
|
||
});
|
||
return row;
|
||
});
|
||
});
|
||
|
||
onMounted(() => {
|
||
generateWeekDates();
|
||
fetchBookings();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<Page>
|
||
<div class="conference-view">
|
||
<!-- 顶部控制区 -->
|
||
<div class="control-panel">
|
||
<Radio.Group v-model:value="viewMode" @change="handleViewModeChange">
|
||
<Radio.Button value="date">按日期</Radio.Button>
|
||
<Radio.Button value="room">按会议室</Radio.Button>
|
||
</Radio.Group>
|
||
<Select
|
||
v-if="viewMode === 'room'"
|
||
v-model:value="selectedRoom"
|
||
class="room-select"
|
||
placeholder="请选择会议室"
|
||
@change="handleRoomChange"
|
||
>
|
||
<Select.Option
|
||
v-for="room in roomList"
|
||
:key="room.id"
|
||
:value="room.id"
|
||
>
|
||
{{ room.name }}
|
||
</Select.Option>
|
||
</Select>
|
||
</div>
|
||
<div v-if="viewMode === 'date'" class="date-buttons">
|
||
<Button
|
||
v-for="date in weekDates"
|
||
:key="date"
|
||
:type="date === selectedDate ? 'primary' : 'default'"
|
||
@click="handleDateChange(date)"
|
||
>
|
||
{{ dayjs(date).format('YYYY-MM-DD') }}
|
||
</Button>
|
||
</div>
|
||
<Table
|
||
:columns="columns"
|
||
:data-source="tableData"
|
||
:pagination="false"
|
||
bordered
|
||
:scroll="{ x: 'max-content' }"
|
||
:loading="loading"
|
||
tableLayout="fixed"
|
||
>
|
||
<template #bodyCell="{ column, record }">
|
||
<template v-if="column.dataIndex !== 'slot'">
|
||
<div
|
||
v-if="record[column.dataIndex]"
|
||
:style="getRandomBgColor()"
|
||
>
|
||
<div>预约人:{{ record[column.dataIndex].personName }}</div>
|
||
<div>单位:{{ record[column.dataIndex].unitName }}</div>
|
||
</div>
|
||
<div v-else></div>
|
||
</template>
|
||
</template>
|
||
</Table>
|
||
</div>
|
||
</Page>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
.conference-view {
|
||
.control-panel {
|
||
margin-bottom: 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
.room-select {
|
||
width: 200px;
|
||
margin-left: 16px;
|
||
}
|
||
}
|
||
|
||
.date-buttons {
|
||
margin-top: 16px;
|
||
display: flex;
|
||
justify-content: center;
|
||
:deep(.ant-btn) {
|
||
margin-right: 8px;
|
||
|
||
&:last-child {
|
||
margin-right: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.booking-cell {
|
||
background: #e6f7ff;
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
min-height: 60px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
span {
|
||
display: block;
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
|
||
&.booking-user {
|
||
color: #1890ff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
&.booking-dept {
|
||
color: #666;
|
||
}
|
||
|
||
&.booking-subject {
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
&:hover {
|
||
background: #bae7ff;
|
||
}
|
||
}
|
||
|
||
:deep(.ant-table-tbody > tr > td:first-child) {
|
||
padding: 10px;
|
||
height: 200px;
|
||
}
|
||
}
|
||
</style>
|