访客邀约

This commit is contained in:
weiwei committed 2025-08-01 18:52:35 +08:00
1 parent 61bdaf3c9c
commit 659820d000
11 files changed
+509 -471

No files matched your search

+162 -166
View File
@@ -1,238 +1,234 @@
<template>
<view class="container">
<!-- 顶部自定义导航栏 -->
<view class="custom-nav">
<view class="nav-back" @tap="goBack">
<text class="iconfont icon-fanhui"></text> 返回
</view>
<view class="nav-title">预定会议室</view>
</view>
<view class="container">
<!-- 顶部自定义导航栏 -->
<view class="custom-nav">
<view class="nav-back" @tap="goBack"> <text class="iconfont icon-fanhui"></text> 返回 </view>
<view class="nav-title">预定会议室</view>
</view>
<!-- 日期选择栏(简化版) -->
<view class="date-bar">
<text class="date-text">{{ todayText }}</text>
<text class="date-arrow">▼</text>
</view>
<!-- 日期选择栏(简化版) -->
<view class="date-bar">
<text class="date-text">{{ todayText }}</text>
<text class="date-arrow">▼</text>
</view>
<!-- 会议室列表 -->
<view v-for="room in meetingRooms" :key="room.id" class="room-card" @tap="goDetail(room)">
<view class="room-info">
<view class="room-main">
<view class="room-title">
{{ room.roomName }} <text class="room-capacity">{{ room.capacity }}人</text>
</view>
<view class="room-address">
<text class="iconfont">📍</text> {{ room.location || '未知地址' }}
</view>
<view class="room-tags">
<text v-for="eq in room.equipmentNames" :key="eq.name" class="room-tag">
{{ eq.name }}<span v-if="eq.quantity"> x{{ eq.quantity }}</span>
</text>
</view>
</view>
<image :src="room.picUrl || '/static/meeting/default.jpg'" class="room-img" mode="aspectFill"></image>
</view>
<!-- 时间条 -->
<view class="timeline-bar">
<view v-for="(slot, idx) in room.timeline" :key="idx" :class="['timeline-slot', slot]" />
</view>
<view class="timeline-labels">
<text v-for="h in 17" :key="h" class="timeline-label">{{ h + 7 }}</text>
</view>
</view>
</view>
<MeetingTabbar activeTab="home" />
<!-- 会议室列表 -->
<view v-for="room in meetingRooms" :key="room.id" class="room-card" @tap="goDetail(room)">
<view class="room-info">
<view class="room-main">
<view class="room-title">
{{ room.roomName }} <text class="room-capacity">{{ room.capacity }}人</text>
</view>
<view class="room-address"> <text class="iconfont">📍</text> {{ room.location || "未知地址" }} </view>
<view class="room-tags">
<text v-for="eq in room.equipmentNames" :key="eq.name" class="room-tag">
{{ eq.name }}<span v-if="eq.quantity"> x{{ eq.quantity }}</span>
</text>
</view>
</view>
<image :src="room.picUrl || '/static/meeting/default.jpg'" class="room-img" mode="aspectFill"></image>
</view>
<!-- 时间条 -->
<view class="timeline-bar">
<view v-for="(slot, idx) in room.timeline" :key="idx" :class="['timeline-slot', slot]" />
</view>
<view class="timeline-labels">
<text v-for="h in 17" :key="h" class="timeline-label">{{ h + 7 }}</text>
</view>
</view>
</view>
<MeetingTabbar activeTab="home" />
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import meetingRoomApi from '@/sheep/api/meetingRoom'
import MeetingTabbar from '@/components/MeetingTabbar.vue'
import { ref, onMounted } from "vue"
import meetingRoomApi from "@/sheep/api/meetingRoom"
import MeetingTabbar from "@/sheep/components/MeetingTabbar.vue"
const goBack = () => {
uni.navigateBack()
uni.navigateBack()
}
const todayText = ref('')
const todayText = ref("")
const meetingRooms = ref<any[]>([])
onMounted(() => {
todayText.value = getTodayText()
fetchMeetingRooms()
todayText.value = getTodayText()
fetchMeetingRooms()
})
function getTodayText() {
const d = new Date()
const week = ['日','一','二','三','四','五','六']
return `${d.getMonth()+1}月${d.getDate()}日 周${week[d.getDay()]}`
const d = new Date()
const week = ["日", "一", "二", "三", "四", "五", "六"]
return `${d.getMonth() + 1}月${d.getDate()}日 周${week[d.getDay()]}`
}
function fetchMeetingRooms() {
meetingRoomApi.getMeetingRoomList({ current: 1, size: 10 }).then((res: any) => {
if (res.code === 0) {
meetingRooms.value = res.data.records
}
})
meetingRoomApi.getMeetingRoomList({ current: 1, size: 10 }).then((res: any) => {
if (res.code === 0) {
meetingRooms.value = res.data.records
}
})
}
function goDetail(room) {
console.log('index.vue: Navigating to detail page with room:', room)
console.log('index.vue: Room ID being passed:', room.id)
uni.navigateTo({
url: `/pages/integrated/meeting/detail?roomId=${room.id}`,
success: function(res) {
// 通过 eventChannel 传递 room 对象
res.eventChannel.emit('acceptRoomInfo', room)
console.log('index.vue: Room object emitted via eventChannel:', room)
}
})
console.log("index.vue: Navigating to detail page with room:", room)
console.log("index.vue: Room ID being passed:", room.id)
uni.navigateTo({
url: `/pages/integrated/meeting/detail?roomId=${room.id}`,
success: function (res) {
// 通过 eventChannel 传递 room 对象
res.eventChannel.emit("acceptRoomInfo", room)
console.log("index.vue: Room object emitted via eventChannel:", room)
},
})
}
</script>
<style lang="less" scoped>
@import url('//at.alicdn.com/t/font_1234567_lingdang.css'); /* 替换为你的iconfont地址 */
@import url("//at.alicdn.com/t/font_1234567_lingdang.css"); /* 替换为你的iconfont地址 */
.container {
min-height: 100vh;
background: #f5f6fa;
padding-bottom: 32rpx;
min-height: 100vh;
background: #f5f6fa;
padding-bottom: 32rpx;
}
.custom-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 88rpx;
background: #fff;
display: flex;
align-items: center;
border-bottom: 1px solid #f0f0f0;
z-index: 100;
position: fixed;
top: 0;
left: 0;
right: 0;
height: 88rpx;
background: #fff;
display: flex;
align-items: center;
border-bottom: 1px solid #f0f0f0;
z-index: 100;
}
.nav-back {
padding-left: 24rpx;
padding-top: 12rpx;
width: auto;
height: 60rpx;
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 30rpx;
color: #1976d2;
cursor: pointer;
padding-left: 24rpx;
padding-top: 12rpx;
width: auto;
height: 60rpx;
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 30rpx;
color: #1976d2;
cursor: pointer;
}
.nav-back .iconfont {
font-size: 36rpx;
margin-right: 8rpx;
font-size: 36rpx;
margin-right: 8rpx;
}
.nav-title {
flex: 1;
text-align: center;
color: #222;
font-size: 32rpx;
font-weight: 500;
margin-right: 60rpx;
flex: 1;
text-align: center;
color: #222;
font-size: 32rpx;
font-weight: 500;
margin-right: 60rpx;
}
.date-bar {
margin-top: 88rpx;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 32rpx;
height: 60rpx;
background: #fff;
border-bottom: 1px solid #f0f0f0;
margin-top: 88rpx;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 32rpx;
height: 60rpx;
background: #fff;
border-bottom: 1px solid #f0f0f0;
}
.date-text {
color: #222;
font-size: 28rpx;
margin-right: 8rpx;
color: #222;
font-size: 28rpx;
margin-right: 8rpx;
}
.date-arrow {
color: #bbb;
font-size: 24rpx;
color: #bbb;
font-size: 24rpx;
}
.room-card {
margin: 24rpx 24rpx 0 24rpx;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
padding: 24rpx;
margin: 24rpx 24rpx 0 24rpx;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
padding: 24rpx;
}
.room-info {
display: flex;
flex-direction: row;
align-items: flex-start;
display: flex;
flex-direction: row;
align-items: flex-start;
}
.room-main {
flex: 1;
flex: 1;
}
.room-title {
font-size: 32rpx;
font-weight: 600;
color: #222;
margin-bottom: 8rpx;
font-size: 32rpx;
font-weight: 600;
color: #222;
margin-bottom: 8rpx;
}
.room-capacity {
font-size: 24rpx;
color: #888;
margin-left: 8rpx;
font-size: 24rpx;
color: #888;
margin-left: 8rpx;
}
.room-address {
font-size: 24rpx;
color: #888;
margin-bottom: 8rpx;
font-size: 24rpx;
color: #888;
margin-bottom: 8rpx;
}
.room-tags {
margin-bottom: 8rpx;
margin-bottom: 8rpx;
}
.room-tag {
display: inline-block;
background: #f2f6ff;
color: #1777FF;
font-size: 22rpx;
border-radius: 8rpx;
padding: 4rpx 16rpx;
margin-right: 12rpx;
margin-bottom: 4rpx;
display: inline-block;
background: #f2f6ff;
color: #1777ff;
font-size: 22rpx;
border-radius: 8rpx;
padding: 4rpx 16rpx;
margin-right: 12rpx;
margin-bottom: 4rpx;
}
.room-img {
width: 160rpx;
height: 100rpx;
border-radius: 8rpx;
margin-left: 24rpx;
object-fit: cover;
background: #eee;
width: 160rpx;
height: 100rpx;
border-radius: 8rpx;
margin-left: 24rpx;
object-fit: cover;
background: #eee;
}
.timeline-bar {
display: flex;
flex-direction: row;
align-items: center;
margin: 16rpx 0 0 0;
height: 16rpx;
display: flex;
flex-direction: row;
align-items: center;
margin: 16rpx 0 0 0;
height: 16rpx;
}
.timeline-slot {
flex: 1;
height: 100%;
margin-right: 2rpx;
border-radius: 4rpx;
background: #e6eaf2;
flex: 1;
height: 100%;
margin-right: 2rpx;
border-radius: 4rpx;
background: #e6eaf2;
}
.timeline-slot.busy {
background: #ffb74d;
background: #ffb74d;
}
.timeline-slot.free {
background: #4d8fff;
background: #4d8fff;
}
.timeline-labels {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 8rpx;
display: flex;
flex-direction: row;
align-items: center;
margin-top: 8rpx;
}
.timeline-label {
flex: 1;
text-align: center;
color: #bbb;
font-size: 20rpx;
flex: 1;
text-align: center;
color: #bbb;
font-size: 20rpx;
}
</style>
</style>
+161 -163
View File
@@ -1,58 +1,51 @@
<template>
<view class="container">
<!-- 顶部自定义导航栏 -->
<view class="custom-nav">
<view class="nav-title">我的预约</view>
</view>
<view class="container">
<!-- 顶部自定义导航栏 -->
<view class="custom-nav">
<view class="nav-title">我的预约</view>
</view>
<!-- 顶部tab -->
<view class="tabs">
<view
v-for="(tab, idx) in tabs"
:key="tab"
:class="['tab', idx === activeTab ? 'active' : '']"
@tap="onTabChange(idx)"
>{{ tab }}</view>
</view>
<!-- 顶部tab -->
<view class="tabs">
<view v-for="(tab, idx) in tabs" :key="tab" :class="['tab', idx === activeTab ? 'active' : '']" @tap="onTabChange(idx)">{{
tab
}}</view>
</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>
<!-- 预约卡片列表 -->
<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>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import meetingRoomApi from '@/sheep/api/meetingRoom'
import MeetingTabbar from '@/components/MeetingTabbar.vue'
import { ref, onMounted } from "vue"
import meetingRoomApi from "@/sheep/api/meetingRoom"
import MeetingTabbar from "@/sheep/components/MeetingTabbar.vue"
const tabs = ['全部', '审核中', '审核成功', '已拒绝', '已取消']
const tabs = ["全部", "审核中", "审核成功", "已拒绝", "已取消"]
const activeTab = ref(0)
const reservationList = ref<any[]>([])
const loading = ref(false)
@@ -61,181 +54,186 @@ const current = ref(1)
const size = ref(10)
const statusMap = {
1: { text: '审核中', class: 'pending' },
2: { text: '审核成功', class: 'done' },
3: { text: '已拒绝', class: 'cancel' },
4: { text: '已取消', class: 'cancel' }
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
})
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()
fetchReservations()
})
function onTabChange(idx: number) {
activeTab.value = idx
current.value = 1
fetchReservations()
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' })
}
}
})
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
})
uni.navigateTo({
url: "/pages/integrated/meeting/booking-detail?id=" + item.id,
})
}
</script>
<style lang="less" scoped>
.container {
min-height: 100vh;
background: #f5f6fa;
padding-bottom: 120rpx;
min-height: 100vh;
background: #f5f6fa;
padding-bottom: 120rpx;
}
.custom-nav {
height: 88rpx;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 600;
border-bottom: 1px solid #f0f0f0;
height: 88rpx;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 600;
border-bottom: 1px solid #f0f0f0;
}
.tabs {
display: flex;
background: #fff;
border-bottom: 1px solid #f0f0f0;
display: flex;
background: #fff;
border-bottom: 1px solid #f0f0f0;
}
.tab {
flex: 1;
text-align: center;
padding: 24rpx 0;
font-size: 28rpx;
color: #888;
position: relative;
flex: 1;
text-align: center;
padding: 24rpx 0;
font-size: 28rpx;
color: #888;
position: relative;
}
.tab.active {
color: #1777FF;
font-weight: 600;
color: #1777ff;
font-weight: 600;
}
.tab.active::after {
content: '';
display: block;
width: 60rpx;
height: 6rpx;
background: #1777FF;
border-radius: 3rpx;
margin: 0 auto;
position: absolute;
left: 0; right: 0; bottom: 0;
content: "";
display: block;
width: 60rpx;
height: 6rpx;
background: #1777ff;
border-radius: 3rpx;
margin: 0 auto;
position: absolute;
left: 0;
right: 0;
bottom: 0;
}
.card {
margin: 24rpx;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
padding: 24rpx;
margin-bottom: 16rpx;
margin: 24rpx;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
padding: 24rpx;
margin-bottom: 16rpx;
}
.card-header {
display: flex;
align-items: center;
margin-bottom: 12rpx;
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.card-icon {
width: 40rpx;
height: 40rpx;
margin-right: 12rpx;
width: 40rpx;
height: 40rpx;
margin-right: 12rpx;
}
.card-title {
font-size: 30rpx;
font-weight: 600;
flex: 1;
font-size: 30rpx;
font-weight: 600;
flex: 1;
}
.card-status {
font-size: 26rpx;
margin-left: 12rpx;
font-size: 26rpx;
margin-left: 12rpx;
}
.card-status.done {
color: #888;
color: #888;
}
.card-status.pending {
color: #1777FF;
color: #1777ff;
}
.card-status.cancel {
color: #ff4d4f;
color: #ff4d4f;
}
.card-info {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
line-height: 1.7;
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
line-height: 1.7;
}
.card-footer {
display: flex;
gap: 24rpx;
display: flex;
gap: 24rpx;
}
.card-btn {
background: #f2f6ff;
color: #1777FF;
border-radius: 8rpx;
padding: 8rpx 24rpx;
font-size: 24rpx;
background: #f2f6ff;
color: #1777ff;
border-radius: 8rpx;
padding: 8rpx 24rpx;
font-size: 24rpx;
}
.card-btn.cancel {
background: #ffeaea;
color: #ff4d4f;
background: #ffeaea;
color: #ff4d4f;
}
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 120rpx;
color: #bbb;
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;
width: 180rpx;
height: 180rpx;
margin-bottom: 24rpx;
}
.empty-text {
font-size: 30rpx;
font-weight: 600;
margin-bottom: 8rpx;
font-size: 30rpx;
font-weight: 600;
margin-bottom: 8rpx;
}
.empty-tip {
font-size: 24rpx;
color: #aaa;
font-size: 24rpx;
color: #aaa;
}
</style>
</style>
+156 -99
View File
@@ -5,50 +5,40 @@
<view class="mx-auto mb-1 text-center text-lg">访客邀约</view>
<tm-alert :border="['2']" :borderColor="['#89c0ff']" :darkBorderColor="['#3b5066']">访客邀约请填写以下信息</tm-alert>
<tm-form @submit="onsubmit" v-model="form" :rules="rules" direction="vertical">
<tm-form-item name="name" label="访客姓名">
<!-- <tm-form-item name="name" label="访客姓名">
<tm-input color="transparent" v-model.trim="form.name" placeholder="请输入真实姓名" />
</tm-form-item>
</tm-form-item> -->
<tm-form-item name="mobile" label="访客手机号">
<tm-input color="transparent" v-model.trim="form.mobile" placeholder="请输入访客手机号" />
</tm-form-item>
<tm-form-item name="companyName" label="来访公司名称">
<!-- <tm-form-item name="companyName" label="来访公司名称">
<tm-input color="transparent" v-model.trim="form.companyName" placeholder="来访公司名称" />
</tm-form-item> -->
<tm-form-item name="vStartDate" label="拜访开始时间 >" @click="startDatePicker('vStartDate')">
<template #label>
<span>拜访开始时间 </span>
<tm-icon name="arrow-right-s-line" />
</template>
<tm-input
:modelValue="form.vStartDate"
placeholder="请选择拜访开始时间"
style="pointer-events: none"
disabled
color="transparent"
/>
</tm-form-item>
<tm-form-item name="vStartDate" label="拜访开始时间">
<tm-picker-date
v-model="form.vStartDate"
v-model:modelStr="_timestr"
type="minute"
start="2020-01-01 00:00"
end="2080-01-01 23:59"
>
<tm-input
:modelValue="form.vStartDate"
placeholder="请选择拜访开始时间"
style="pointer-events: none"
disabled
color="transparent"
>
<template v-slot:rightIcon>
<tm-icon name="arrow-right-s-line"></tm-icon>
</template>
</tm-input>
</tm-picker-date>
</tm-form-item>
<tm-form-item name="vEndDate" label="拜访离开时间">
<tm-picker-date v-model="form.vEndDate" v-model:modelStr="_timestr" type="minute" start="2020-01-01 00:00" end="2080-01-01 23:59">
<tm-input
:modelValue="form.vEndDate"
placeholder="请选择拜访离开时间"
style="pointer-events: none"
disabled
color="transparent"
>
<template v-slot:rightIcon>
<tm-icon name="arrow-right-s-line"></tm-icon>
</template>
</tm-input>
</tm-picker-date>
<tm-form-item name="vEndDate" label="拜访离开时间 >" @click="endDatePicker('vEndDate')">
<template #label>
<span>拜访离开时间 </span>
<tm-icon name="arrow-right-s-line" />
</template>
<tm-input
:modelValue="form.vEndDate"
placeholder="请选择拜访离开时间"
style="pointer-events: none"
disabled
color="transparent"
/>
</tm-form-item>
<!-- <tm-form-item name="deptId" label="拜访部门">
@@ -64,7 +54,7 @@
</tm-drawer>
</tm-form-item> -->
<tm-form-item name="reasonValue" label="拜访原因">
<!-- <tm-form-item name="reasonValue" label="拜访原因">
<tm-picker @confirm="reasonPickerConfirm" :list="visitorReasonList">
<tm-input :modelValue="form.reasonText" placeholder="请选择拜访原因" style="pointer-events: none" disabled color="transparent">
<template #rightIcon>
@@ -72,7 +62,8 @@
</template>
</tm-input>
</tm-picker>
</tm-form-item>
</tm-form-item> -->
<tm-form-item name="isOtherPeople" label="是否可随行人员" direction="horizontal" labelWidth="500">
<tm-switch v-model="form.isOtherPeople" />
</tm-form-item>
@@ -82,107 +73,173 @@
<tm-button form-type="submit" block>提交</tm-button>
</tm-form>
</tm-sheet>
<tm-drawer
title="请选择时间"
:showClose="true"
:showFooter="false"
size="auto"
placement="bottom"
v-model:show="showDatePicker"
:duration="100"
zIndex="100"
>
<time-range-picker @confirm="onDatePickerConfirm" @cancel="showDatePicker = false" />
</tm-drawer>
<tm-modal
v-model:show="modalShow"
:overlayClick="false"
title="提示"
position="bottom"
height="auto"
:showCancel="true"
cancelText="取消"
confirmText="确定"
@confirm="confirmSubmit"
@cancel="modalShow = false"
>
<view class="text-center"><tm-text font-size="30" color="#999999">确认提交吗?</tm-text></view>
</tm-modal>
<template #main-bottom>
<view class="h-100rpx flex justify-around items-center">
<view class="flex-col justify-center items-center">
<image src="@/static/page/invitation-select.png" class="w-50rpx h-50rpx" />
<view class="text-center text-24 c-#1977FD">访客邀约</view>
</view>
<view class="flex-col justify-center items-center" @click="goInvitationRecord">
<image src="@/static/page/order.png" class="w-50rpx h-50rpx" />
<view class="text-center text-24 c-#999999">邀约记录</view>
</view>
</view>
</template>
</crud-layout>
</template>
<script setup lang="ts">
import { onLoad } from "@dcloudio/uni-app"
import { reactive, ref } from "vue"
import { invitationApi } from "@/sheep/api/index"
import TimeRangePicker from "@/sheep/components/TimePointPicker.vue"
const form = reactive<any>({
name: "",
// name: "",
mobile: "",
companyName: "",
// companyName: "",
vStartDate: "",
vEndDate: "",
reasonValue: "",
reasonText: "",
// reasonValue: "",
// reasonText: "",
isOtherPeople: true,
isDrive: true,
})
const _timestr = ref("2025-01-01")
const visitorReasonList = ref<{ title: string; id: string }[]>([])
const showDatePicker = ref(false)
const curSelectedInputName = ref("")
const modalShow = ref(false)
// const visitorReasonList = ref<{ title: string; id: string }[]>([])
onLoad(() => {
getVisitorReasonList()
})
// onLoad(() => {
// getVisitorReasonList()
// })
// 获取拜访原因列表
const getVisitorReasonList = async () => {
const { code, data } = await invitationApi.getDictListByType("1")
if (code == 0 && data) {
visitorReasonList.value = data.map((item: any) => ({ title: item.label, id: item.value }))
return
}
visitorReasonList.value = []
}
// const getVisitorReasonList = async () => {
// const { code, data } = await invitationApi.getDictListByType("1")
// if (code == 0 && data) {
// visitorReasonList.value = data.map((item: any) => ({ title: item.label, id: item.value }))
// return
// }
// visitorReasonList.value = []
// }
// 来访原因选择器确认
const reasonPickerConfirm = (ids: string) => {
const reason = visitorReasonList.value.find((item) => item.id === ids[0])
form.reasonText = reason?.title || ""
form.reasonValue = reason?.id || ""
}
// const reasonPickerConfirm = (ids: string) => {
// const reason = visitorReasonList.value.find((item) => item.id === ids[0])
// form.reasonText = reason?.title || ""
// form.reasonValue = reason?.id || ""
// }
const rules: Record<string, TM.FORM_RULE[]> = {
name: [{ required: true, message: "姓名2到5个字符", rule: /^.{2,5}$/ }],
// name: [{ required: true, message: "姓名2到5个字符", rule: /^.{2,5}$/ }],
mobile: [{ required: true, message: "请输入正确的手机号", rule: /^(?:(?:\+|00)86)?1\d{10}$/ }],
companyName: [{ required: true, message: "请输入来访公司名称" }],
// companyName: [{ required: true, message: "请输入来访公司名称" }],
vStartDate: [{ required: true, message: "请选择拜访开始时间" }],
vEndDate: [{ required: true, message: "请选择拜访离开时间" }],
reasonValue: [{ required: true, message: "请选择拜访原因" }],
// reasonValue: [{ required: true, message: "请选择拜访原因" }],
}
// 开始日期选择
const startDatePicker = (key: string) => {
showDatePicker.value = true
curSelectedInputName.value = key
}
// 离开日期选择
const endDatePicker = (key: string) => {
showDatePicker.value = true
curSelectedInputName.value = key
}
// 日期选择器确认
const onDatePickerConfirm = (dateValue: any, timeValue: string) => {
form[curSelectedInputName.value] = `${dateValue.fullDate} ${timeValue}`
showDatePicker.value = false
}
// 表单提交
const onsubmit = (evt: TM.FORM_SUBMIT_RESULT) => {
// console.log("校验结果:", evt)
if (!evt.isPass) return
uni.showModal({
title: "提示",
content: "确认提交吗?",
confirmColor: "#0094ff",
success: async () => {
if (form.isOtherPeople) {
form.isOtherPeople = "1"
} else {
form.isOtherPeople = "0"
}
modalShow.value = true
}
if (form.isDrive) {
form.isDrive = "1"
} else {
form.isDrive = "0"
}
// 确认提交
const confirmSubmit = async () => {
if (form.isOtherPeople) {
form.isOtherPeople = "1"
} else {
form.isOtherPeople = "0"
}
try {
const { code, data, msg } = await invitationApi.saveInvitation(form)
if (code == 0) {
uni.showToast({ title: "提交成功", icon: "success" })
resetForm()
uni.switchTab({ url: "/pages/tabbar/invitationRecord" })
return
}
uni.showToast({ title: msg || "提交失败", icon: "none" })
} catch (error: any) {
uni.showToast({ title: error.message || "提交失败", icon: "none" })
}
},
})
if (form.isDrive) {
form.isDrive = "1"
} else {
form.isDrive = "0"
}
form.mobile = form.mobile.trim().replace(/\s/g, "")
try {
const { code, data, msg } = await invitationApi.saveInvitation(form)
if (code == 0) {
uni.showToast({ title: "提交成功", icon: "success" })
resetForm()
modalShow.value = false
uni.navigateTo({ url: "/pages/integrated/visitor/invitationRecord" })
return
}
uni.showToast({ title: msg || "提交失败", icon: "none" })
} catch (error: any) {
uni.showToast({ title: error.message || "提交失败", icon: "none" })
}
}
// 重置表单
const resetForm = () => {
Object.assign(form, {
name: "",
// name: "",
mobile: "",
companyName: "",
// companyName: "",
vStartDate: "",
vEndDate: "",
reasonValue: "",
reasonText: "",
// reasonValue: "",
// reasonText: "",
isOtherPeople: true,
isDrive: true,
})
}
const goInvitationRecord = () => {
uni.redirectTo({ url: "/pages/integrated/visitor/invitationRecord" })
}
</script>
@@ -15,7 +15,7 @@
@scroll="pageHook.scroll"
>
<template #top>
<view class="bg-white rounded-20rpx my-20rpx font-bold py-1">
<!-- <view class="bg-white rounded-20rpx my-20rpx font-bold py-1">
<z-tabs
:list="[
{ name: '访客申请', value: 0 },
@@ -44,8 +44,9 @@
}"
@change="onTabChange"
/>
</view>
</view> -->
<view class="h-20rpx" />
<view class="bg-white rounded-16rpx px-20rpx">
<z-tabs :list="tabs" @change="doTab" />
</view>
@@ -72,18 +73,31 @@
</view>
</view>
<view class="flex justify-end items-center gap-2">
<view v-if="v.checkStatus == 1" class="flex items-center gap-2">
<!-- <view v-if="v.checkStatus == 1" class="flex items-center gap-2">
<tm-modal title="确定审核通过吗?" height="auto" @confirm="visitorCheck(v.id, 2)">
<template v-slot:trigger><tm-button class="px-1" size="s">审核通过</tm-button></template>
</tm-modal>
<tm-modal title="确定审核拒绝吗?" height="auto" @confirm="visitorCheck(v.id, 3)">
<template v-slot:trigger><tm-button class="px-1" size="s" color="danger">审核拒绝</tm-button></template>
</tm-modal>
</view>
</view> -->
<tm-button class="px-1" size="s" color="success" @click.stop="goDetail(v)">查看详情</tm-button>
</view>
</view>
</z-paging>
<template #main-bottom>
<view class="h-100rpx flex justify-around items-center">
<view class="flex-col justify-center items-center" @click="goInvitation">
<image src="@/static/page/invitation.png" class="w-50rpx h-50rpx" />
<view class="text-center text-24 c-#999999">访客邀约</view>
</view>
<view class="flex-col justify-center items-center">
<image src="@/static/page/order-select.png" class="w-50rpx h-50rpx" />
<view class="text-center text-24 c-#1977FD">邀约记录</view>
</view>
</view>
</template>
</crud-layout>
</template>
@@ -105,7 +119,7 @@ const tabs = [
const state = reactive<any>({
params: {
checkStatus: tabs[0].value,
type: 1, // type为1:主动申请 type为2:邀约 type为3:后台创建
type: 2, // type为1:主动申请 type为2:邀约 type为3:后台创建
},
})
const pageRef = ref<any>(null)
@@ -147,10 +161,10 @@ const query = (page: number, pagesize: number) => {
pageHook.startPage(page, pagesize, false, state.params)
}
const onTabChange = (e: number) => {
state.params.type = e + 1
pageRef.value.reload(false)
}
// const onTabChange = (e: number) => {
// state.params.type = e + 1
// pageRef.value.reload(false)
// }
// 审核通过/拒绝
const visitorCheck = async (id: string, checkStatus: number | string) => {
@@ -168,7 +182,11 @@ const visitorCheck = async (id: string, checkStatus: number | string) => {
}
// 查看详情
const goDetail = (v) => {
router.jump("/pages/invitation/detail", { id: v.id, token: v.token })
const goDetail = (v: any) => {
router.jump("/pages/integrated/visitor/detail", { id: v.id, token: v.token })
}
const goInvitation = () => {
uni.redirectTo({ url: "/pages/integrated/visitor/invitation" })
}
</script>
+1 -1
View File
@@ -58,7 +58,7 @@ const services = [
},
{
name: "visitor",
label: "访客管理",
label: "访客邀约",
icon: "/src/static/icon/visitor.png",
url: "/pages/integrated/visitor/invitation",
},
File renamed without changes.
-31
View File
@@ -1,31 +0,0 @@
<template>
<view class="flex-row h-100rpx bg-#f6f8fa px-10rpx py-10rpx rounded-16rpx">
<view
class="w-50% flex-row-center color-#818898 font-size-28rpx! rounded-16rpx"
:style="{ color: index == cur ? '#fff' : '#818898', backgroundColor: index == cur ? '#00a624' : '#f6f8fa' }"
v-for="(item, index) in tablist"
@click="changeTab(index)"
>
{{ item.name }}
</view>
</view>
</template>
<script setup lang="ts">
import { reactive, ref } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
let cur = ref(0)
let tablist = ref([
{
name: "我的订单",
},
{
name: "历史订单",
},
])
onLoad(() => {})
const changeTab = (index: number) => {
cur.value = index
}
</script>
File renamed without changes.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B