综合服务-会议模块页面

This commit is contained in:
zjf committed 2025-06-26 18:43:05 +08:00
1 parent db303eb0a9
commit 7a12dba8b4
7 files changed
+239 -116

No files matched your search

+6
View File
@@ -478,6 +478,12 @@
"style": {
"navigationBarTitleText": "我的预约"
}
},
{
"path": "meeting/booking-detail",
"style": {
"navigationBarTitleText": "预约详情"
}
}
]
}
@@ -0,0 +1,97 @@
<template>
<view class="booking-detail">
<view class="room-info">
<view class="room-name">{{ detail.roomName }}</view>
<view class="room-meta">
<text>容量:{{ detail.capacity }}</text>
<text>位置:{{ detail.location }}</text>
</view>
<view v-if="detail.equipmentNames" class="room-equip">
设备:{{ detail.equipmentNames }}
</view>
</view>
<view class="section">
<view class="section-title">预约信息</view>
<view class="info-row"><text class="label">预约人:</text>{{ detail.username }}</view>
<view class="info-row"><text class="label">预约时间:</text>{{ detail.startTime }} ~ {{ detail.endTime }}</view>
<view class="info-row"><text class="label">预约状态:</text>{{ getStatusText(detail.checkStatus) }}</view>
<view class="info-row" v-if="detail.checkReason"><text class="label">审核意见:</text>{{ detail.checkReason }}</view>
<view class="info-row" v-if="detail.description"><text class="label">备注:</text>{{ detail.description }}</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import meetingRoomApi from '@/sheep/api/meetingRoom'
const detail = ref<any>({})
function getStatusText(status: string) {
const map: Record<string, string> = {
'1': '审核中',
'2': '审核通过',
'3': '审核拒绝',
'4': '已取消',
'5': '已结束',
}
return map[status] || status
}
onLoad(async (options: any) => {
const id = options.id
if (!id) return
const { code, data } = await meetingRoomApi.getBookingDetail(id)
if (code === 0 && data) {
detail.value = data
}
})
</script>
<style lang="scss" scoped>
.booking-detail {
padding: 32rpx;
.room-info {
background: #f7f8fa;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 32rpx;
.room-name {
font-size: 36rpx;
font-weight: bold;
margin-bottom: 12rpx;
}
.room-meta {
font-size: 26rpx;
color: #666;
margin-bottom: 8rpx;
text {
margin-right: 32rpx;
}
}
.room-equip {
font-size: 24rpx;
color: #999;
}
}
.section {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
.section-title {
font-size: 30rpx;
font-weight: bold;
margin-bottom: 18rpx;
}
.info-row {
font-size: 26rpx;
margin-bottom: 14rpx;
.label {
color: #888;
margin-right: 12rpx;
}
}
}
}
</style>
+1 -1
View File
@@ -221,7 +221,7 @@ async function reserveMeeting() {
date,
startTime: startSlot.startTime,
endTime: endSlot.endTime,
title: '会议',
title: room.value.name || '会议',
description: '',
attendees: []
})
+1 -1
View File
@@ -230,4 +230,4 @@ function goDetail(room) {
color: #bbb;
font-size: 20rpx;
}
</style>
</style>
+114 -72
View File
@@ -11,69 +11,108 @@
v-for="(tab, idx) in tabs"
:key="tab"
:class="['tab', idx === activeTab ? 'active' : '']"
@tap="activeTab = idx"
@tap="onTabChange(idx)"
>{{ tab }}</view>
</view>
<!-- 优惠礼包(可选) -->
<view class="coupon-box">
<text class="coupon-title">优惠礼包</text>
<text class="coupon-desc">还未购买过优惠礼包</text>
<text class="arrow">&gt;</text>
</view>
<!-- 预约卡片列表 -->
<view v-for="item in reservationList" :key="item.id" class="card">
<view class="card-header">
<image src="/static/icons/meeting.png" class="card-icon" />
<text class="card-title">{{ item.roomName }} → {{ item.location }}</text>
<text class="card-status" :class="item.statusClass">{{ item.statusText }}</text>
</view>
<view class="card-info">
<view>预约时间:{{ item.time }}</view>
<view>会议主题:{{ item.subject }}</view>
<view>参会人:{{ item.attendees }}</view>
</view>
<view class="card-footer">
<view class="card-btn" v-for="btn in item.actions" :key="btn">{{ btn }}</view>
<view v-if="reservationList.length">
<view v-for="item in reservationList" :key="item.id" class="card">
<view class="card-header">
<image src="/static/icons/meeting.png" class="card-icon" />
<text class="card-title">{{ item.title }}</text>
<text class="card-status" :class="statusMap[item.checkStatus]?.class">{{ statusMap[item.checkStatus]?.text }}</text>
</view>
<view class="card-info">
<view>预约日期:{{ item.bookingDate }}</view>
<view>时间:{{ item.startTime }} ~ {{ item.endTime }}</view>
<view>申请人:{{ item.createBy }}</view>
</view>
<view class="card-footer">
<view class="card-btn" @tap="viewDetail(item)">查看详情</view>
<view
v-if="item.checkStatus === 1 || item.checkStatus === 2"
class="card-btn cancel"
@tap="cancelBooking(item)"
>取消预约</view>
</view>
</view>
</view>
<view v-else class="empty">
<image src="/static/icons/empty.png" class="empty-img" />
<view class="empty-text">暂无预约</view>
<view class="empty-tip">您还没有预约会议,快去预约吧!</view>
</view>
<MeetingTabbar activeTab="mine" />
</view>
<MeetingTabbar activeTab="mine" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, onMounted } from 'vue'
import meetingRoomApi from '@/sheep/api/meetingRoom'
import MeetingTabbar from '@/components/MeetingTabbar.vue'
const tabs = ['全部', '未开始', '已结束', '已取消']
const tabs = ['全部', '审核中', '审核成功', '已拒绝', '已取消']
const activeTab = ref(0)
const reservationList = ref<any[]>([])
const loading = ref(false)
const total = ref(0)
const current = ref(1)
const size = ref(10)
// 静态假数据,等你有接口后再对接
const reservationList = ref([
{
id: 1,
roomName: '一号会议室',
location: '总部大厦A座',
time: '2025-06-20 14:00~15:00',
subject: '周例会',
attendees: '张三、李四、王五',
statusText: '已结束',
statusClass: 'done',
actions: ['更多', '再次预约']
},
{
id: 2,
roomName: '二号会议室',
location: '总部大厦B座',
time: '2025-06-22 09:00~10:00',
subject: '项目讨论',
attendees: '张三、赵六',
statusText: '未开始',
statusClass: 'pending',
actions: ['取消预约', '查看详情']
}
])
const statusMap = {
1: { text: '审核中', class: 'pending' },
2: { text: '审核成功', class: 'done' },
3: { text: '已拒绝', class: 'cancel' },
4: { text: '已取消', class: 'cancel' }
}
function fetchReservations() {
loading.value = true
let checkStatus = activeTab.value === 0 ? undefined : activeTab.value // 0为全部
meetingRoomApi.getMyBookings({
current: current.value,
size: size.value,
...(checkStatus ? { checkStatus } : {})
}).then((res: any) => {
loading.value = false
if (res.code === 0 && res.data && res.data.records) {
reservationList.value = res.data.records
total.value = res.data.total
}
}).catch(() => {
loading.value = false
})
}
onMounted(() => {
fetchReservations()
})
function onTabChange(idx: number) {
activeTab.value = idx
current.value = 1
fetchReservations()
}
function cancelBooking(item: any) {
uni.showModal({
title: '提示',
content: '确定要取消该预约吗?',
success: (res) => {
if (res.confirm) {
// TODO: 调用取消预约接口
uni.showToast({ title: '已取消(待接入接口)', icon: 'none' })
}
}
})
}
function viewDetail(item: any) {
uni.navigateTo({
url: '/pages/integrated/meeting/booking-detail?id=' + item.id
})
}
</script>
<style lang="less" scoped>
@@ -120,29 +159,6 @@ const reservationList = ref([
position: absolute;
left: 0; right: 0; bottom: 0;
}
.coupon-box {
margin: 24rpx;
background: #fffbe6;
border-radius: 12rpx;
padding: 24rpx;
display: flex;
align-items: center;
font-size: 26rpx;
color: #bfa243;
position: relative;
}
.coupon-title {
font-weight: 600;
margin-right: 16rpx;
}
.coupon-desc {
flex: 1;
}
.arrow {
color: #bfa243;
font-size: 32rpx;
margin-left: 16rpx;
}
.card {
margin: 24rpx;
background: #fff;
@@ -196,4 +212,30 @@ const reservationList = ref([
padding: 8rpx 24rpx;
font-size: 24rpx;
}
.card-btn.cancel {
background: #ffeaea;
color: #ff4d4f;
}
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 120rpx;
color: #bbb;
}
.empty-img {
width: 180rpx;
height: 180rpx;
margin-bottom: 24rpx;
}
.empty-text {
font-size: 30rpx;
font-weight: 600;
margin-bottom: 8rpx;
}
.empty-tip {
font-size: 24rpx;
color: #aaa;
}
</style>
+1 -42
View File
@@ -59,16 +59,7 @@
<view v-if="!loading && visitorList.length === 0" class="empty">暂无访客记录</view>
</scroll-view>
<view class="tabbar">
<view class="tabbar-item" @tap="goHome">
<image src="/static/icons/home.png" class="tabbar-icon" />
<text>首页</text>
</view>
<view class="tabbar-item" @tap="goMine">
<image src="/static/icons/mine.png" class="tabbar-icon" />
<text>我的</text>
</view>
</view>
<tabbar :tabBarList="tabBarList" :current="tabbarStore.current" />
</view>
</template>
@@ -489,37 +480,5 @@ onMounted(() => {
color: #999;
font-size: 26rpx;
}
.tabbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
height: 100rpx;
background: #fff;
display: flex;
border-top: 1px solid #eee;
z-index: 999;
}
.tabbar-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #888;
font-size: 24rpx;
}
.tabbar-icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 4rpx;
}
.tabbar-item.active {
color: #1777FF;
}
}
</style>
+19
View File
@@ -37,4 +37,23 @@ export default {
method: "POST",
data,
}),
/**
* 获取我的预约列表
*/
getMyBookings: (params: { current: number; size: number; checkStatus?: number }) =>
request({
url: '/integrated/meetingRoom/app/my-bookings',
method: 'GET',
params,
}),
/**
* 获取预约详情
*/
getBookingDetail: (id: string) =>
request({
url: `/integrated/meetingRoom/app/booking-detail/${id}`,
method: 'GET',
}),
}