Merge branch 'master' of https://codeup.aliyun.com/bsdtec/biz-pc-cloud
This commit is contained in:
commit
c12338fd0d
4 files changed
+252
-47
No files matched your search
@@ -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 }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user