This commit is contained in:
weiwei committed 2025-07-03 19:20:51 +08:00
commit c12338fd0d
4 files changed
+252 -47

No files matched your search

+9
View File
@@ -39,3 +39,12 @@ export function putObj(obj?: Object) {
})
}
// 获取会议室某天的时间段预约状态
export function getMeetingRoomTimeSlots(roomId: string, date: string) {
return request({
url: `/integrated/meetingRoom/${roomId}/time-slots`,
method: 'get',
params: { date }
})
}
+6 -5
View File
@@ -36,11 +36,12 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="描述" prop="description">
<editor v-if="visible" v-model:get-html="form.description" placeholder="请输入描述" :readonly="readonly"></editor>
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="描述" prop="description">
<editor v-if="visible" v-model:get-html="form.description" placeholder="请输入描述" :readonly="readonly"></editor>
</el-form-item>
</el-col>
</el-row>
</el-form>
+232 -37
View File
@@ -10,8 +10,10 @@
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="会议室id" prop="roomId">
<el-input v-model="form.roomId" placeholder="请输入会议室id"/>
<el-form-item label="会议室" prop="roomId">
<el-select v-model="form.roomId" placeholder="请选择会议室" style="width: 100%;">
<el-option v-for="room in roomList" :key="room.id" :label="room.name" :value="room.id" />
</el-select>
</el-form-item>
</el-col>
@@ -27,37 +29,103 @@
</el-form-item>
</el-col>
<!-- <el-col :span="12" class="mb20">-->
<!-- <el-form-item label="申请人id" prop="applicantId">-->
<!-- <el-input v-model="form.applicantId" placeholder="请选择申请人"/>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<el-col :span="12" class="mb20">
<el-form-item label="部门id" prop="deptId">
<el-input v-model="form.deptId" placeholder="请输入部门id"/>
<el-form-item label="会议负责人" prop="assigneeId">
<el-input v-model="form.assigneeId" placeholder="请输入会议负责人"/>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="申请人id" prop="applicantId">
<el-input v-model="form.applicantId" placeholder="请输入申请人id"/>
</el-form-item>
<!-- 会议日期+开始/结束时间同一行 -->
<el-row :gutter="24" class="mb20">
<el-col :span="8">
<el-form-item label="会议日期" prop="bookingDate">
<el-date-picker
v-model="form.bookingDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择会议日期"
style="width: 100%;"
:readonly="true"
:disabled="true"
/>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="会议负责人id" prop="assigneeId">
<el-input v-model="form.assigneeId" placeholder="请输入会议负责人id"/>
</el-form-item>
<el-col :span="8">
<el-form-item label="开始时间" prop="startTime">
<el-time-select
v-model="form.startTime"
:picker-options="{
start: '08:00',
end: '18:00',
step: '00:30'
}"
placeholder="请选择开始时间"
style="width: 100%;"
:readonly="true"
:disabled="true"
/>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="开始时间" prop="startTime">
<el-input v-model="form.startTime" placeholder="请输入开始时间"/>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="结束时间" prop="endTime">
<el-input v-model="form.endTime" placeholder="请输入结束时间"/>
</el-form-item>
<el-col :span="8">
<el-form-item label="结束时间" prop="endTime">
<el-time-select
v-model="form.endTime"
:picker-options="{
start: '08:00',
end: '18:00',
step: '00:30'
}"
placeholder="请选择结束时间"
style="width: 100%;"
:readonly="true"
:disabled="true"
/>
</el-form-item>
</el-col>
</el-row>
</el-row>
<!-- 新增:会议时间段预约列表,带日期tab -->
<div v-if="form.roomId" style="margin: 20px 0;">
<div style="font-weight: bold; margin-bottom: 8px;">可预约时间段</div>
<el-tabs v-model="activeDate" @tab-click="onDateTabChange">
<el-tab-pane v-for="date in dateTabs" :key="date.value" :label="date.label" :name="date.value">
<template v-if="Array.isArray(timeSlots) && timeSlots.length">
<el-row v-for="slot in timeSlots" :key="slot.startTime + '-' + slot.endTime" style="margin-bottom: 8px;">
<el-col :span="8">
<span>{{ slot.startTime }} - {{ slot.endTime }}</span>
</el-col>
<el-col :span="8">
<template v-if="slot.status === 'booked'">
<el-tag type="danger">已预约: {{ slot.bookedBy?.userName }}</el-tag>
</template>
<template v-else>
<el-button
:type="selectedSlots.includes(slot.startTime + '-' + slot.endTime) ? 'success' : 'primary'"
:plain="!selectedSlots.includes(slot.startTime + '-' + slot.endTime)"
size="small"
@click="toggleSlot(slot)"
>
{{ selectedSlots.includes(slot.startTime + '-' + slot.endTime) ? '已选中' : '可预约' }}
</el-button>
</template>
</el-col>
</el-row>
</template>
<template v-else>
<div style="color: #999; padding: 16px 0;">暂无可预约时间段</div>
</template>
</el-tab-pane>
</el-tabs>
</div>
<div v-else style="margin: 20px 0; color: #f56c6c;">请先选择会议室</div>
</el-form>
<template #footer>
<span class="dialog-footer">
@@ -71,14 +139,25 @@
<script setup lang="ts" name="MeetingBookingDialog">
import { useDict } from '/@/hooks/dict';
import { useMessage } from "/@/hooks/message";
import { getObj, addObj, putObj } from '/@/api/integrated/meetingBooking'
import { getObj, addObj, putObj, getMeetingRoomTimeSlots } from '/@/api/integrated/meetingBooking'
import { rule } from '/@/utils/validate';
import axios from 'axios'
import { ref, onMounted, watch } from 'vue'
import dayjs from 'dayjs'
import { ElMessage } from 'element-plus';
const emit = defineEmits(['refresh']);
// 定义变量内容
const dataFormRef = ref();
const visible = ref(false)
const loading = ref(false)
const timeSlots = ref<any[]>([])
const activeDate = ref(dayjs().format('YYYY-MM-DD'))
const dateTabs = ref<{label: string, value: string}[]>([])
const roomList = ref<{id: string, name: string}[]>([])
const selectedSlots = ref<string[]>([])
// 定义字典
// 提交表单数据
@@ -93,28 +172,29 @@ const form = reactive({
assigneeId: '',
startTime: '',
endTime: '',
bookingDate:''
});
// 定义校验规则
const dataRules = ref({
title: [{required: true, message: '会议主题不能为空', trigger: 'blur'}],
roomId: [{required: true, message: '会议室id不能为空', trigger: 'blur'}],
bookingDate: [{required: true, message: '会议日期不能为空', trigger: 'change'}],
startTime: [{required: true, message: '开始时间不能为空', trigger: 'change'}],
endTime: [{required: true, message: '结束时间不能为空', trigger: 'change'}],
})
// 打开弹窗
const openDialog = (id: string) => {
visible.value = true
form.id = ''
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields();
});
// 获取meetingBooking信息
visible.value = true;
form.id = '';
nextTick(async () => {
dataFormRef.value?.resetFields();
await fetchRoomList();
});
if (id) {
form.id = id
getmeetingBookingData(id)
form.id = id;
getmeetingBookingData(id);
}
};
@@ -148,8 +228,123 @@ const getmeetingBookingData = (id: string) => {
})
};
// 生成今天及往后6天tab
const genDateTabs = () => {
dateTabs.value = Array.from({ length: 7 }, (_, i) => {
const date = dayjs().add(i, 'day')
let label = date.format('MM-DD')
if (i === 0) label = '今天'
else if (i === 1) label = '明天'
else if (i === 2) label = '后天'
return { label, value: date.format('YYYY-MM-DD') }
})
}
genDateTabs()
const fetchTimeSlots = async () => {
if (!form.roomId || !activeDate.value) {
timeSlots.value = [];
return;
}
const res = await getMeetingRoomTimeSlots(form.roomId, activeDate.value);
// 强制转为普通数组,避免Proxy导致渲染异常
const arr = Array.isArray(res.data?.data) ? [...res.data.data] : [];
console.log('接口返回', res);
console.log('最终渲染', arr);
timeSlots.value = res.data;
}
// 监听 activeDate 变化
watch(activeDate, () => {
fetchTimeSlots();
});
// 监听 roomId 变化
watch(() => form.roomId, () => {
fetchTimeSlots();
});
// tab切换事件
const onDateTabChange = () => {
fetchTimeSlots();
// 自动填充会议日期
form.bookingDate = activeDate.value;
};
function areSlotsContinuous(slots: string[]): boolean {
if (slots.length <= 1) return true;
const sorted = slots
.map(key => key.split('-').concat(key))
.map(([start, end, key]) => ({ start, end, key: key as string }))
.sort((a, b) => a.start.localeCompare(b.start));
for (let i = 1; i < sorted.length; i++) {
if (sorted[i - 1].end !== sorted[i].start) {
return false;
}
}
return true;
}
const toggleSlot = (slot: any) => {
const key = slot.startTime + '-' + slot.endTime;
const idx = selectedSlots.value.indexOf(key);
let newSelected = [...selectedSlots.value];
if (idx > -1) {
// 只允许取消首尾
const sorted = newSelected
.map(key => key.split('-'))
.map(([start, end]) => ({ start, end, key }))
.sort((a, b) => a.start.localeCompare(b.start));
if (key !== sorted[0].key && key !== sorted[sorted.length - 1].key) {
ElMessage.warning('只能取消首尾的时间段');
return;
}
newSelected.splice(idx, 1);
} else {
newSelected.push(key);
// 检查是否连续
if (!areSlotsContinuous(newSelected)) {
ElMessage.warning('只能选择连续的时间段');
return;
}
}
selectedSlots.value = newSelected;
// 自动填充表单的开始和结束时间
if (selectedSlots.value.length) {
const times = selectedSlots.value
.map(key => key.split('-'))
.map(([start, end]) => ({ start, end }));
const sorted = times.sort((a, b) => a.start.localeCompare(b.start));
form.startTime = sorted[0].start;
form.endTime = sorted[sorted.length - 1].end;
form.bookingDate = activeDate.value;
} else {
form.startTime = '';
form.endTime = '';
}
};
const isSelected = (slot: any) => {
const key = slot.startTime + '-' + slot.endTime;
return selectedSlots.value.includes(key);
};
// 假数据,实际应请求接口
const fetchRoomList = async () => {
roomList.value = [
{ id: '1925061129836544002', name: '会议室A' },
{ id: '2', name: '会议室B' }
];
if (roomList.value.length && !form.roomId) {
form.roomId = roomList.value[0].id;
}
}
// 暴露变量
defineExpose({
openDialog
});
</script>
</script>
@@ -29,7 +29,7 @@
@queryTable="getDataList"></right-toolbar>
</div>
</el-row>
<el-table :data="state.dataList" v-loading="state.loading" border
<el-table :data="state.dataList" v-loading="state.loading" border
:cell-style="tableStyle.cellStyle" :header-cell-style="tableStyle.headerCellStyle"
@selection-change="selectionChangHandle"
@sort-change="sortChangeHandle">
@@ -39,9 +39,9 @@
<el-table-column prop="roomId" label="会议室id" show-overflow-tooltip/>
<el-table-column prop="description" label="会议说明" show-overflow-tooltip/>
<el-table-column prop="meetingStatus" label="审批状态" show-overflow-tooltip/>
<el-table-column prop="deptId" label="部门id" show-overflow-tooltip/>
<el-table-column prop="applicantId" label="申请人id" show-overflow-tooltip/>
<el-table-column prop="assigneeId" label="会议负责人id" show-overflow-tooltip/>
<el-table-column prop="applicantId" 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="startTime" label="开始时间" show-overflow-tooltip/>
<el-table-column prop="endTime" label="结束时间" show-overflow-tooltip/>
<el-table-column label="操作" width="150">
@@ -131,4 +131,4 @@ const handleDelete = async (ids: string[]) => {
useMessage().error(err.msg);
}
};
</script>
</script>