Merge branch 'master' of https://gitea.zianxin.com/zax/biz-mobile-react-cloud
No files matched your search
@@ -85,35 +85,11 @@ const tabbar: TabbarItem[] = [
|
||||
auth: ["merchant"],
|
||||
},
|
||||
{
|
||||
pagePath: "pages/tabbar/invitation",
|
||||
text: "访客邀约",
|
||||
iconPath: "/static/tabbar/invitation.png",
|
||||
selectedIconPath: "/static/tabbar/invitation-select.png",
|
||||
launchParam: ["invitation"],
|
||||
auth: [],
|
||||
},
|
||||
{
|
||||
pagePath: "pages/tabbar/invitationRecord",
|
||||
text: "邀约记录",
|
||||
iconPath: "/static/tabbar/invitation.png",
|
||||
selectedIconPath: "/static/tabbar/invitation-select.png",
|
||||
launchParam: ["invitation"],
|
||||
auth: [],
|
||||
},
|
||||
{
|
||||
pagePath: "pages/tabbar/message",
|
||||
text: "通知公告",
|
||||
iconPath: "/static/tabbar/message.png",
|
||||
selectedIconPath: "/static/tabbar/message-select.png",
|
||||
launchParam: ["message"],
|
||||
auth: [],
|
||||
},
|
||||
{
|
||||
pagePath: "pages/integrated/index",
|
||||
pagePath: "pages/tabbar/integrated",
|
||||
text: "综合服务",
|
||||
iconPath: "/static/tabbar/integrated.png",
|
||||
selectedIconPath: "/static/tabbar/integrated-select.png",
|
||||
launchParam: [],
|
||||
launchParam: ["integrated"],
|
||||
auth: [],
|
||||
},
|
||||
{
|
||||
@@ -121,7 +97,7 @@ const tabbar: TabbarItem[] = [
|
||||
text: "我的",
|
||||
iconPath: "/static/tabbar/my.png",
|
||||
selectedIconPath: "/static/tabbar/my-select.png",
|
||||
launchParam: ["canteen", "selfMerchant", "onlineShop", "coopMerchant", "invitation"],
|
||||
launchParam: ["canteen", "selfMerchant", "onlineShop", "coopMerchant", "integrated"],
|
||||
auth: ["user", "merchant", "doorAdmin"],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -73,23 +73,9 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabbar/invitation",
|
||||
"path": "pages/tabbar/integrated",
|
||||
"style": {
|
||||
"navigationBarTitleText": "访客",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabbar/invitationRecord",
|
||||
"style": {
|
||||
"navigationBarTitleText": "访客记录",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tabbar/message",
|
||||
"style": {
|
||||
"navigationBarTitleText": "通知公告",
|
||||
"navigationBarTitleText": "综合服务",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
@@ -414,24 +400,13 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/invitation",
|
||||
"pages": [
|
||||
{
|
||||
"path": "detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "访客详情"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/thirdApp",
|
||||
"pages": [
|
||||
{
|
||||
"path": "thirdAppList",
|
||||
"path": "happyMall",
|
||||
"style": {
|
||||
"navigationBarTitleText": "第三方应用"
|
||||
"navigationBarTitleText": "美好商城"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -439,12 +414,6 @@
|
||||
{
|
||||
"root": "pages/integrated",
|
||||
"pages": [
|
||||
{
|
||||
"path": "index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "综合服务"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "lostAndFound/index",
|
||||
"style": {
|
||||
@@ -494,15 +463,21 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "visitor/index",
|
||||
"path": "visitor/invitation",
|
||||
"style": {
|
||||
"navigationBarTitleText": "访客管理"
|
||||
"navigationBarTitleText": "访客邀约"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "visitor/invitationRecord",
|
||||
"style": {
|
||||
"navigationBarTitleText": "邀约记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "visitor/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "访客详情"
|
||||
"navigationBarTitleText": "邀约详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -596,25 +571,7 @@
|
||||
"selectedIconPath": ""
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/tabbar/invitation",
|
||||
"text": "",
|
||||
"iconPath": "",
|
||||
"selectedIconPath": ""
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/tabbar/invitationRecord",
|
||||
"text": "",
|
||||
"iconPath": "",
|
||||
"selectedIconPath": ""
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/tabbar/message",
|
||||
"text": "",
|
||||
"iconPath": "",
|
||||
"selectedIconPath": ""
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/integrated/index",
|
||||
"pagePath": "pages/tabbar/integrated",
|
||||
"text": "",
|
||||
"iconPath": "",
|
||||
"selectedIconPath": ""
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
<template>
|
||||
<div class="integrated-title-card">
|
||||
<div class="integrated-title">综合服务</div>
|
||||
</div>
|
||||
<div class="service-container-card">
|
||||
<div class="service-container">
|
||||
<div class="service-grid">
|
||||
<div
|
||||
v-for="item in services"
|
||||
:key="item.name"
|
||||
class="service-item"
|
||||
@click="goPage(item)"
|
||||
>
|
||||
<image :src="item.icon" class="service-icon" />
|
||||
<div class="service-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const services = [
|
||||
{
|
||||
name: 'todo',
|
||||
label: '工作待办',
|
||||
icon: '/src/static/icon/todo.png',
|
||||
url: '/pages/integrated/todo/index'
|
||||
},
|
||||
{
|
||||
name: 'meeting-booking',
|
||||
label: '会议预定',
|
||||
icon: '/src/static/icon/meeting-booking.png',
|
||||
url: '/pages/integrated/meeting/index'
|
||||
},
|
||||
{
|
||||
name: 'lost-and-found',
|
||||
label: '失物招领',
|
||||
icon: '/src/static/icon/lost-and-found.png',
|
||||
url: '/pages/integrated/lostAndFound/index'
|
||||
},
|
||||
{
|
||||
name: 'feedback',
|
||||
label: '意见反馈',
|
||||
icon: '/src/static/icon/feedback.png',
|
||||
url: '/pages/integrated/feedback/index'
|
||||
},
|
||||
{
|
||||
name: 'repair-report',
|
||||
label: '维修上报',
|
||||
icon: '/src/static/icon/repair-report.png',
|
||||
url: '/pages/integrated/repair/index'
|
||||
},
|
||||
{
|
||||
name: 'notice',
|
||||
label: '通知公告',
|
||||
icon: '/src/static/icon/notice.png',
|
||||
url: '/pages/integrated/notice/index'
|
||||
},
|
||||
{
|
||||
name: 'visitor',
|
||||
label: '访客管理',
|
||||
icon: '/src/static/icon/visitor.png',
|
||||
url: '/pages/integrated/visitor/index'
|
||||
}
|
||||
]
|
||||
|
||||
function goPage(item) {
|
||||
if (item.url) {
|
||||
uni.navigateTo({ url: item.url })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.integrated-title-card {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.06);
|
||||
margin: 32rpx 24rpx 0 24rpx;
|
||||
padding: 0 0 8rpx 0;
|
||||
}
|
||||
.integrated-title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
text-align: center;
|
||||
padding: 36rpx 0 24rpx 0;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
.service-container-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.06);
|
||||
margin: 24rpx;
|
||||
padding-bottom: 32rpx;
|
||||
}
|
||||
.service-container {
|
||||
padding: 32rpx 0 0 0;
|
||||
background: transparent;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
.service-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
.service-item {
|
||||
width: 25%;
|
||||
margin-bottom: 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.service-item:hover {
|
||||
transform: translateY(-6rpx) scale(1.06);
|
||||
box-shadow: 0 2rpx 12rpx rgba(25,118,210,0.08);
|
||||
}
|
||||
.service-icon {
|
||||
width: 92rpx;
|
||||
height: 92rpx;
|
||||
margin-bottom: 14rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #f5f7fa;
|
||||
box-shadow: 0 2rpx 8rpx rgba(25,118,210,0.04);
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
.service-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
body {
|
||||
background: #f5f6fa;
|
||||
}
|
||||
</style>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -1,236 +1,105 @@
|
||||
<template>
|
||||
<view class="visitor-detail">
|
||||
<view class="detail-header">
|
||||
<view class="visitor-info">
|
||||
<text class="visitor-name">{{ detail.visitorName }}</text>
|
||||
<text class="visitor-phone">{{ detail.visitorPhone }}</text>
|
||||
</view>
|
||||
<view class="status-tag" :class="getStatusClass(detail.status)">{{ getStatusText(detail.status) }}</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-content">
|
||||
<view class="info-row">
|
||||
<text class="label">来访目的:</text>
|
||||
<text class="value">{{ getPurposeLabel(detail.purpose) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">被访人:</text>
|
||||
<text class="value">{{ detail.hostName }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">来访时间:</text>
|
||||
<text class="value">{{ formatVisitTime(detail.visitDate, detail.visitTime) }}</text>
|
||||
</view>
|
||||
<view v-if="detail.remark" class="info-row">
|
||||
<text class="label">备注说明:</text>
|
||||
<text class="value">{{ detail.remark }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="detail.rejectReason" class="reject-section">
|
||||
<view class="reject-title">拒绝原因</view>
|
||||
<view class="reject-content">{{ detail.rejectReason }}</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-footer">
|
||||
<text class="apply-time">申请时间:{{ formatTime(detail.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<crud-layout title="详情" mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<view v-if="detail.id">
|
||||
<tm-sheet :margin="[0, 20]" class="second-text-32">
|
||||
<tm-text font-size="32">访客信息</tm-text>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>访客姓名:</text>
|
||||
<text>{{ detail.name }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>访客手机号:</text>
|
||||
<text>{{ detail.mobile }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>来访公司名称:</text>
|
||||
<text>{{ detail.companyName }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>来访原因:</text>
|
||||
<text>{{ visitorReasonList.find((item) => item.id == detail.reasonValue)?.title || "" }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>接待人:</text>
|
||||
<text>{{ detail.userName }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>来访人数:</text>
|
||||
<text>{{ detail.peopleNumber }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>是否可随行人员:</text>
|
||||
<text>{{ detail.isOtherPeople == "1" ? "是" : "否" }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>是否可驾驶车辆:</text>
|
||||
<text>{{ detail.isDrive == "1" ? "是" : "否" }}</text>
|
||||
</view>
|
||||
</tm-sheet>
|
||||
|
||||
<tm-sheet :margin="[0, 20]" class="second-text-32">
|
||||
<tm-text font-size="32">时间</tm-text>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>预计来访时间:</text>
|
||||
<text>{{ detail.vendDate }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>预计离开时间:</text>
|
||||
<text>{{ detail.vstartDate }}</text>
|
||||
</view>
|
||||
</tm-sheet>
|
||||
|
||||
<tm-sheet :margin="[0, 20]" class="second-text-32">
|
||||
<tm-text font-size="32">其他</tm-text>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>创建方式:</text>
|
||||
<text>{{ detail.type == "1" ? "主动申请" : detail.type == "2" ? "邀约" : "后台创建" }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between mt-1">
|
||||
<text>提交时间:</text>
|
||||
<text>{{ detail.createTime }}</text>
|
||||
</view>
|
||||
</tm-sheet>
|
||||
</view>
|
||||
|
||||
<view class="h-40rpx" />
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import meetingRoomApi from '@/sheep/api/meetingRoom'
|
||||
import { onMounted, ref } from "vue"
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { invitationApi } from "@/sheep/api"
|
||||
|
||||
const detail = ref<any>({})
|
||||
|
||||
const purposeOptions = [
|
||||
{ value: '1', label: '商务洽谈' },
|
||||
{ value: '2', label: '技术交流' },
|
||||
{ value: '3', label: '面试' },
|
||||
{ value: '4', label: '参观访问' },
|
||||
{ value: '5', label: '其他' }
|
||||
]
|
||||
|
||||
const timeOptions = [
|
||||
{ value: '1', label: '上午 (9:00-12:00)' },
|
||||
{ value: '2', label: '下午 (14:00-18:00)' },
|
||||
{ value: '3', label: '全天 (9:00-18:00)' }
|
||||
]
|
||||
|
||||
function getPurposeLabel(purpose: string) {
|
||||
const option = purposeOptions.find(item => item.value === purpose)
|
||||
return option ? option.label : '其他'
|
||||
}
|
||||
|
||||
function getStatusText(status: string) {
|
||||
const statusMap: Record<string, string> = {
|
||||
'1': '待审核',
|
||||
'2': '已通过',
|
||||
'3': '已拒绝',
|
||||
'4': '已取消',
|
||||
'5': '已完成'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
|
||||
function getStatusClass(status: string) {
|
||||
const classMap: Record<string, string> = {
|
||||
'1': 'status-pending',
|
||||
'2': 'status-approved',
|
||||
'3': 'status-rejected',
|
||||
'4': 'status-cancelled',
|
||||
'5': 'status-completed'
|
||||
}
|
||||
return classMap[status] || 'status-pending'
|
||||
}
|
||||
|
||||
function formatVisitTime(date: string, time: string) {
|
||||
const timeLabel = timeOptions.find(item => item.value === time)?.label || ''
|
||||
return `${date} ${timeLabel}`
|
||||
}
|
||||
|
||||
function formatTime(time: string) {
|
||||
if (!time) return ''
|
||||
return new Date(time).toLocaleString()
|
||||
}
|
||||
const visitorReasonList = ref<{ title: string; id: string }[]>([])
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
const id = options.id
|
||||
if (!id) return
|
||||
|
||||
try {
|
||||
const response: any = await meetingRoomApi.getVisitorDetail(id)
|
||||
if (response.code === 0 && response.data) {
|
||||
detail.value = response.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取访客详情失败:', error)
|
||||
uni.showToast({ title: '获取详情失败', icon: 'none' })
|
||||
}
|
||||
const id = options.id
|
||||
|
||||
try {
|
||||
const { code, data, msg } = await invitationApi.getInvitationDetail(id)
|
||||
if (code == 0) {
|
||||
detail.value = data
|
||||
return
|
||||
}
|
||||
uni.showToast({ title: msg || "获取详情失败", icon: "none" })
|
||||
} catch (error: any) {
|
||||
uni.showToast({ title: error.message, icon: "none" })
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
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 = []
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.visitor-detail {
|
||||
padding: 32rpx;
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.visitor-info {
|
||||
.visitor-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.visitor-phone {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 12rpx 24rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 26rpx;
|
||||
|
||||
&.status-pending {
|
||||
background: #fff3e0;
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
&.status-approved {
|
||||
background: #e8f5e8;
|
||||
color: #388e3c;
|
||||
}
|
||||
|
||||
&.status-rejected {
|
||||
background: #ffebee;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
&.status-cancelled {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&.status-completed {
|
||||
background: #e3f2fd;
|
||||
color: #1976d2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
width: 160rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reject-section {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.reject-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #d32f2f;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.reject-content {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-footer {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
|
||||
.apply-time {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,438 +0,0 @@
|
||||
<template>
|
||||
<view class="visitor-container">
|
||||
<!-- 访客申请表单 -->
|
||||
<view class="visitor-form">
|
||||
<view class="form-title">访客申请</view>
|
||||
<view class="form-item">
|
||||
<view class="label">访客姓名</view>
|
||||
<input v-model="visitorName" placeholder="请输入访客姓名" class="name-input" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">访客手机号</view>
|
||||
<input v-model="visitorPhone" placeholder="请输入访客手机号" class="phone-input" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">来访目的</view>
|
||||
<picker @change="onPurposeChange" :value="purposeIndex" :range="purposeOptions" range-key="label">
|
||||
<view class="picker">{{ purposeOptions[purposeIndex].label }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">来访时间</view>
|
||||
<picker mode="date" @change="onDateChange" :value="visitDate">
|
||||
<view class="picker">{{ visitDate || '请选择来访日期' }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">来访时段</view>
|
||||
<picker @change="onTimeChange" :value="timeIndex" :range="timeOptions" range-key="label">
|
||||
<view class="picker">{{ timeOptions[timeIndex].label }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">被访人</view>
|
||||
<input v-model="hostName" placeholder="请输入被访人姓名" class="host-input" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">备注说明</view>
|
||||
<textarea
|
||||
v-model="remark"
|
||||
placeholder="请输入备注说明(选填)"
|
||||
class="remark-textarea"
|
||||
maxlength="200"
|
||||
/>
|
||||
<view class="char-count">{{ remark.length }}/200</view>
|
||||
</view>
|
||||
<button @click="submitVisitor" class="submit-btn" :disabled="!canSubmit">
|
||||
提交申请
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 访客记录列表 -->
|
||||
<view class="visitor-list">
|
||||
<view class="list-title">访客记录</view>
|
||||
<view v-if="visitorList.length === 0" class="empty-tip">
|
||||
暂无访客记录
|
||||
</view>
|
||||
<view v-else>
|
||||
<view
|
||||
v-for="item in visitorList"
|
||||
:key="item.id"
|
||||
class="visitor-item"
|
||||
@click="viewDetail(item)"
|
||||
>
|
||||
<view class="item-header">
|
||||
<view class="visitor-info">
|
||||
<text class="visitor-name">{{ item.visitorName }}</text>
|
||||
<text class="visitor-phone">{{ item.visitorPhone }}</text>
|
||||
</view>
|
||||
<view class="status-tag" :class="getStatusClass(item.status)">{{ getStatusText(item.status) }}</view>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="purpose">来访目的:{{ getPurposeLabel(item.purpose) }}</view>
|
||||
<view class="host">被访人:{{ item.hostName }}</view>
|
||||
<view class="time">来访时间:{{ formatVisitTime(item.visitDate, item.visitTime) }}</view>
|
||||
</view>
|
||||
<view class="item-footer">
|
||||
<text class="apply-time">申请时间:{{ formatTime(item.createTime) }}</text>
|
||||
<text v-if="item.remark" class="has-remark">有备注</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import meetingRoomApi from '@/sheep/api/meetingRoom'
|
||||
|
||||
const purposeOptions = [
|
||||
{ value: '1', label: '商务洽谈' },
|
||||
{ value: '2', label: '技术交流' },
|
||||
{ value: '3', label: '面试' },
|
||||
{ value: '4', label: '参观访问' },
|
||||
{ value: '5', label: '其他' }
|
||||
]
|
||||
|
||||
const timeOptions = [
|
||||
{ value: '1', label: '上午 (9:00-12:00)' },
|
||||
{ value: '2', label: '下午 (14:00-18:00)' },
|
||||
{ value: '3', label: '全天 (9:00-18:00)' }
|
||||
]
|
||||
|
||||
const visitorName = ref('')
|
||||
const visitorPhone = ref('')
|
||||
const purposeIndex = ref(0)
|
||||
const visitDate = ref('')
|
||||
const timeIndex = ref(0)
|
||||
const hostName = ref('')
|
||||
const remark = ref('')
|
||||
const visitorList = ref<any[]>([])
|
||||
|
||||
const canSubmit = computed(() => {
|
||||
return visitorName.value.trim() &&
|
||||
visitorPhone.value.trim() &&
|
||||
visitDate.value &&
|
||||
hostName.value.trim()
|
||||
})
|
||||
|
||||
function onPurposeChange(e: any) {
|
||||
purposeIndex.value = e.detail.value
|
||||
}
|
||||
|
||||
function onDateChange(e: any) {
|
||||
visitDate.value = e.detail.value
|
||||
}
|
||||
|
||||
function onTimeChange(e: any) {
|
||||
timeIndex.value = e.detail.value
|
||||
}
|
||||
|
||||
function getPurposeLabel(purpose: string) {
|
||||
const option = purposeOptions.find(item => item.value === purpose)
|
||||
return option ? option.label : '其他'
|
||||
}
|
||||
|
||||
function getStatusText(status: string) {
|
||||
const statusMap: Record<string, string> = {
|
||||
'1': '待审核',
|
||||
'2': '已通过',
|
||||
'3': '已拒绝',
|
||||
'4': '已取消',
|
||||
'5': '已完成'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
|
||||
function getStatusClass(status: string) {
|
||||
const classMap: Record<string, string> = {
|
||||
'1': 'status-pending',
|
||||
'2': 'status-approved',
|
||||
'3': 'status-rejected',
|
||||
'4': 'status-cancelled',
|
||||
'5': 'status-completed'
|
||||
}
|
||||
return classMap[status] || 'status-pending'
|
||||
}
|
||||
|
||||
function formatVisitTime(date: string, time: string) {
|
||||
const timeLabel = timeOptions.find(item => item.value === time)?.label || ''
|
||||
return `${date} ${timeLabel}`
|
||||
}
|
||||
|
||||
function formatTime(time: string) {
|
||||
if (!time) return ''
|
||||
return new Date(time).toLocaleDateString()
|
||||
}
|
||||
|
||||
async function submitVisitor() {
|
||||
if (!canSubmit.value) {
|
||||
uni.showToast({ title: '请填写完整信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 验证手机号格式
|
||||
const phoneRegex = /^1[3-9]\d{9}$/
|
||||
if (!phoneRegex.test(visitorPhone.value)) {
|
||||
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
uni.showLoading({ title: '提交中...' })
|
||||
|
||||
const data = {
|
||||
visitorName: visitorName.value.trim(),
|
||||
visitorPhone: visitorPhone.value.trim(),
|
||||
purpose: purposeOptions[purposeIndex.value].value,
|
||||
visitDate: visitDate.value,
|
||||
visitTime: timeOptions[timeIndex.value].value,
|
||||
hostName: hostName.value.trim(),
|
||||
remark: remark.value.trim()
|
||||
}
|
||||
|
||||
const response: any = await meetingRoomApi.submitVisitor(data)
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
if (response.code === 0) {
|
||||
uni.showToast({ title: '提交成功', icon: 'success' })
|
||||
|
||||
// 清空表单
|
||||
visitorName.value = ''
|
||||
visitorPhone.value = ''
|
||||
purposeIndex.value = 0
|
||||
visitDate.value = ''
|
||||
timeIndex.value = 0
|
||||
hostName.value = ''
|
||||
remark.value = ''
|
||||
|
||||
// 刷新列表
|
||||
loadVisitorList()
|
||||
} else {
|
||||
uni.showToast({ title: response.msg || '提交失败', icon: 'none' })
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: '提交失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
function viewDetail(item: any) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/integrated/visitor/detail?id=${item.id}`
|
||||
})
|
||||
}
|
||||
|
||||
async function loadVisitorList() {
|
||||
try {
|
||||
const response: any = await meetingRoomApi.getVisitorList({
|
||||
current: 1,
|
||||
size: 20
|
||||
})
|
||||
|
||||
if (response.code === 0 && response.data) {
|
||||
visitorList.value = response.data.records || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载访客记录失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadVisitorList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.visitor-container {
|
||||
padding: 24rpx;
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.visitor-form {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.form-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.picker {
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
padding: 0 24rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.name-input,
|
||||
.phone-input,
|
||||
.host-input {
|
||||
height: 80rpx;
|
||||
padding: 0 24rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.remark-textarea {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
padding: 24rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.char-count {
|
||||
text-align: right;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
font-size: 32rpx;
|
||||
|
||||
&:disabled {
|
||||
background: #ccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
|
||||
.list-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
|
||||
.visitor-item {
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
padding: 24rpx 0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.visitor-info {
|
||||
.visitor-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.visitor-phone {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
|
||||
&.status-pending {
|
||||
background: #fff3e0;
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
&.status-approved {
|
||||
background: #e8f5e8;
|
||||
color: #388e3c;
|
||||
}
|
||||
|
||||
&.status-rejected {
|
||||
background: #ffebee;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
&.status-cancelled {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&.status-completed {
|
||||
background: #e3f2fd;
|
||||
color: #1976d2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-content {
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.purpose,
|
||||
.host,
|
||||
.time {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
margin-bottom: 8rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.item-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.apply-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.has-remark {
|
||||
font-size: 24rpx;
|
||||
color: #007aff;
|
||||
background: #e3f2fd;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,245 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<view class="h-20rpx" />
|
||||
<tm-sheet margin="[0]">
|
||||
<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-input color="transparent" v-model.trim="form.name" placeholder="请输入真实姓名" />
|
||||
</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-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="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="拜访部门">
|
||||
<tm-drawer :show-close="true" :show-footer="true" size="auto" title="请选择拜访部门">
|
||||
<template #trigger>
|
||||
<tm-input :modelValue="form.deptId" placeholder="请选择拜访部门" style="pointer-events: none" disabled color="transparent">
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</template>
|
||||
<tm-text color="#999999">精致美观的对话框,可全局配置风格</tm-text>
|
||||
</tm-drawer>
|
||||
</tm-form-item> -->
|
||||
|
||||
<!-- <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>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker>
|
||||
</tm-form-item> -->
|
||||
|
||||
<tm-form-item name="isOtherPeople" label="是否可随行人员" direction="horizontal" labelWidth="500">
|
||||
<tm-switch v-model="form.isOtherPeople" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="isDrive" label="是否可驾驶车辆" direction="horizontal" labelWidth="500">
|
||||
<tm-switch v-model="form.isDrive" />
|
||||
</tm-form-item>
|
||||
<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 { reactive, ref } from "vue"
|
||||
import { invitationApi } from "@/sheep/api/index"
|
||||
import TimeRangePicker from "@/sheep/components/TimePointPicker.vue"
|
||||
|
||||
const form = reactive<any>({
|
||||
// name: "",
|
||||
mobile: "",
|
||||
// companyName: "",
|
||||
vStartDate: "",
|
||||
vEndDate: "",
|
||||
// reasonValue: "",
|
||||
// reasonText: "",
|
||||
isOtherPeople: true,
|
||||
isDrive: true,
|
||||
})
|
||||
const showDatePicker = ref(false)
|
||||
const curSelectedInputName = ref("")
|
||||
const modalShow = ref(false)
|
||||
// const visitorReasonList = ref<{ title: string; id: string }[]>([])
|
||||
|
||||
// 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 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}$/ }],
|
||||
mobile: [{ required: true, message: "请输入正确的手机号", rule: /^(?:(?:\+|00)86)?1\d{10}$/ }],
|
||||
// companyName: [{ required: true, message: "请输入来访公司名称" }],
|
||||
vStartDate: [{ required: true, message: "请选择拜访开始时间" }],
|
||||
vEndDate: [{ 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
|
||||
modalShow.value = true
|
||||
}
|
||||
|
||||
// 确认提交
|
||||
const confirmSubmit = async () => {
|
||||
if (form.isOtherPeople) {
|
||||
form.isOtherPeople = "1"
|
||||
} else {
|
||||
form.isOtherPeople = "0"
|
||||
}
|
||||
|
||||
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: "",
|
||||
mobile: "",
|
||||
// companyName: "",
|
||||
vStartDate: "",
|
||||
vEndDate: "",
|
||||
// reasonValue: "",
|
||||
// reasonText: "",
|
||||
isOtherPeople: true,
|
||||
isDrive: true,
|
||||
})
|
||||
}
|
||||
|
||||
const goInvitationRecord = () => {
|
||||
uni.redirectTo({ url: "/pages/integrated/visitor/invitationRecord" })
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<crud-layout bg="/static/page/blue-bg.png">
|
||||
<z-paging
|
||||
ref="pageRef"
|
||||
height="100%"
|
||||
:fixed="false"
|
||||
v-model="pageHook.list"
|
||||
:default-page-size="10"
|
||||
safe-area-inset-bottom
|
||||
:loading-more-enabled="true"
|
||||
:show-scrollbar="false"
|
||||
@query="query"
|
||||
:auto-clean-list-when-reload="false"
|
||||
:auto-scroll-to-top-when-reload="false"
|
||||
@scroll="pageHook.scroll"
|
||||
>
|
||||
<template #top>
|
||||
<!-- <view class="bg-white rounded-20rpx my-20rpx font-bold py-1">
|
||||
<z-tabs
|
||||
:list="[
|
||||
{ name: '访客申请', value: 0 },
|
||||
{ name: '我的邀约', value: 1 },
|
||||
// { name: '访客申请', value: 0, badge: { count: 10 } },
|
||||
// { name: '我的邀约', value: 1, badge: { count: 6 } },
|
||||
]"
|
||||
bar-width="0"
|
||||
:active-style="{
|
||||
fontSize: '30rpx',
|
||||
backgroundColor: '#ECF4F9',
|
||||
padding: '12rpx 64rpx',
|
||||
borderRadius: '20rpx',
|
||||
border: '1px solid #007AFF',
|
||||
}"
|
||||
:inactive-style="{
|
||||
fontSize: '30rpx',
|
||||
padding: '12rpx 64rpx',
|
||||
border: '1px solid transparent',
|
||||
}"
|
||||
:badge-style="{
|
||||
whiteSpace: 'nowrap',
|
||||
position: 'relative',
|
||||
top: '-14rpx',
|
||||
left: '-72rpx',
|
||||
}"
|
||||
@change="onTabChange"
|
||||
/>
|
||||
</view> -->
|
||||
|
||||
<view class="h-20rpx" />
|
||||
<view class="bg-white rounded-16rpx px-20rpx">
|
||||
<z-tabs :list="tabs" @change="doTab" />
|
||||
</view>
|
||||
<view class="h-20rpx" />
|
||||
</template>
|
||||
<view class="flex-col justify-center card p-3 m-b-20rpx" v-for="(v, i) in pageHook.list" :key="i">
|
||||
<view class="flex justify-between items-center">
|
||||
<view v-if="v.checkStatus == '1'" class="p-1 c-white! sub-text-26 rounded-1 bg-#f5a623">待接受</view>
|
||||
<view v-if="v.checkStatus == '2'" class="p-1 c-white! sub-text-26 rounded-1 bg-#2db7f5">待到访</view>
|
||||
<view v-if="v.checkStatus == '3'" class="p-1 c-white! sub-text-26 rounded-1 bg-#52c41a">已拒绝</view>
|
||||
<view v-if="v.checkStatus == '5'" class="p-1 c-white! sub-text-26 rounded-1 bg-#f5222d">已取消</view>
|
||||
<view class="sub-text-26">{{ v.vstartDate + " ~ " + v.vendDate }}</view>
|
||||
</view>
|
||||
<view class="flex-col my-1 px-4 py-2 bg-#ECF4F9 rounded-4">
|
||||
<view class="flex justify-start sub-text-30 whitespace-nowrap">
|
||||
<view>{{ v.name }}</view>
|
||||
<view class="second-text-28 ml-2">{{ v.mobile }}</view>
|
||||
</view>
|
||||
<view class="flex justify-center sub-text-28 font-italic whitespace-nowrap my-2">
|
||||
<span v-if="v.reasonValue">拜访原因:{{ visitorReasonList.find((item) => item.id == v.reasonValue)?.title || "" }}</span>
|
||||
</view>
|
||||
<!-- <view class="flex justify-end second-text-28 whitespace-nowrap">
|
||||
<span v-if="state.params.type == 1">接待人:{{ v.userName }}</span>
|
||||
</view> -->
|
||||
</view>
|
||||
<view class="flex justify-end 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> -->
|
||||
<tm-button v-if="v.checkStatus == '2'" 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>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from "vue"
|
||||
import { onShow } from "@dcloudio/uni-app"
|
||||
import { hooks, router } from "@@/index"
|
||||
import zTabs from "@@/components/other/z-tabs/z-tabs.vue"
|
||||
import { invitationApi } from "@/sheep/api"
|
||||
|
||||
const tabs = [
|
||||
{ value: "", name: "全部", disabled: false },
|
||||
{ value: "1", name: "待接受", disabled: false },
|
||||
{ value: "2", name: "待到访", disabled: false },
|
||||
{ value: "3", name: "已拒绝", disabled: false },
|
||||
{ value: "5", name: "已取消", disabled: false },
|
||||
]
|
||||
|
||||
const state = reactive<any>({
|
||||
params: {
|
||||
checkStatus: tabs[0].value,
|
||||
type: 2, // type为1:主动申请 type为2:邀约 type为3:后台创建
|
||||
},
|
||||
})
|
||||
const pageRef = ref<any>(null)
|
||||
const visitorReasonList = ref<{ title: string; id: string }[]>([])
|
||||
|
||||
onShow(async () => {
|
||||
const isLogin = await hooks.useCheckLogin()
|
||||
if (!isLogin) return
|
||||
if (pageRef.value) {
|
||||
pageRef.value.reload(false)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
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 doTab = (e: any) => {
|
||||
const target = tabs[e]
|
||||
state.params.checkStatus = target.value
|
||||
pageRef.value.reload(true)
|
||||
}
|
||||
|
||||
const pageHook = hooks.usePage(pageRef, invitationApi.getInvitationRecord, null, "data.records")
|
||||
|
||||
const query = (page: number, pagesize: number) => {
|
||||
const isLogin = hooks.useStaticCheckLogin()
|
||||
if (!isLogin) return
|
||||
pageHook.startPage(page, pagesize, false, state.params)
|
||||
}
|
||||
|
||||
// const onTabChange = (e: number) => {
|
||||
// state.params.type = e + 1
|
||||
// pageRef.value.reload(false)
|
||||
// }
|
||||
|
||||
// 审核通过/拒绝
|
||||
// const visitorCheck = async (id: string, checkStatus: number | string) => {
|
||||
// try {
|
||||
// const { code, msg } = await invitationApi.updateCheckStatus({ id, checkStatus })
|
||||
// if (code == 0) {
|
||||
// uni.showToast({ title: "操作成功", icon: "success" })
|
||||
// pageRef.value.reload(false)
|
||||
// return
|
||||
// }
|
||||
// uni.showToast({ title: msg, icon: "error" })
|
||||
// } catch (error: any) {
|
||||
// uni.showToast({ title: error.message, icon: "error" })
|
||||
// }
|
||||
// }
|
||||
|
||||
// 查看详情
|
||||
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>
|
||||
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<view class="visitor-detail">
|
||||
<view class="detail-header">
|
||||
<view class="visitor-info">
|
||||
<text class="visitor-name">{{ detail.visitorName }}</text>
|
||||
<text class="visitor-phone">{{ detail.visitorPhone }}</text>
|
||||
</view>
|
||||
<view class="status-tag" :class="getStatusClass(detail.status)">{{ getStatusText(detail.status) }}</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-content">
|
||||
<view class="info-row">
|
||||
<text class="label">来访目的:</text>
|
||||
<text class="value">{{ getPurposeLabel(detail.purpose) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">被访人:</text>
|
||||
<text class="value">{{ detail.hostName }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">来访时间:</text>
|
||||
<text class="value">{{ formatVisitTime(detail.visitDate, detail.visitTime) }}</text>
|
||||
</view>
|
||||
<view v-if="detail.remark" class="info-row">
|
||||
<text class="label">备注说明:</text>
|
||||
<text class="value">{{ detail.remark }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="detail.rejectReason" class="reject-section">
|
||||
<view class="reject-title">拒绝原因</view>
|
||||
<view class="reject-content">{{ detail.rejectReason }}</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-footer">
|
||||
<text class="apply-time">申请时间:{{ formatTime(detail.createTime) }}</text>
|
||||
</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>({})
|
||||
|
||||
const purposeOptions = [
|
||||
{ value: '1', label: '商务洽谈' },
|
||||
{ value: '2', label: '技术交流' },
|
||||
{ value: '3', label: '面试' },
|
||||
{ value: '4', label: '参观访问' },
|
||||
{ value: '5', label: '其他' }
|
||||
]
|
||||
|
||||
const timeOptions = [
|
||||
{ value: '1', label: '上午 (9:00-12:00)' },
|
||||
{ value: '2', label: '下午 (14:00-18:00)' },
|
||||
{ value: '3', label: '全天 (9:00-18:00)' }
|
||||
]
|
||||
|
||||
function getPurposeLabel(purpose: string) {
|
||||
const option = purposeOptions.find(item => item.value === purpose)
|
||||
return option ? option.label : '其他'
|
||||
}
|
||||
|
||||
function getStatusText(status: string) {
|
||||
const statusMap: Record<string, string> = {
|
||||
'1': '待审核',
|
||||
'2': '已通过',
|
||||
'3': '已拒绝',
|
||||
'4': '已取消',
|
||||
'5': '已完成'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
|
||||
function getStatusClass(status: string) {
|
||||
const classMap: Record<string, string> = {
|
||||
'1': 'status-pending',
|
||||
'2': 'status-approved',
|
||||
'3': 'status-rejected',
|
||||
'4': 'status-cancelled',
|
||||
'5': 'status-completed'
|
||||
}
|
||||
return classMap[status] || 'status-pending'
|
||||
}
|
||||
|
||||
function formatVisitTime(date: string, time: string) {
|
||||
const timeLabel = timeOptions.find(item => item.value === time)?.label || ''
|
||||
return `${date} ${timeLabel}`
|
||||
}
|
||||
|
||||
function formatTime(time: string) {
|
||||
if (!time) return ''
|
||||
return new Date(time).toLocaleString()
|
||||
}
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
const id = options.id
|
||||
if (!id) return
|
||||
|
||||
try {
|
||||
const response: any = await meetingRoomApi.getVisitorDetail(id)
|
||||
if (response.code === 0 && response.data) {
|
||||
detail.value = response.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取访客详情失败:', error)
|
||||
uni.showToast({ title: '获取详情失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.visitor-detail {
|
||||
padding: 32rpx;
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.visitor-info {
|
||||
.visitor-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.visitor-phone {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 12rpx 24rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 26rpx;
|
||||
|
||||
&.status-pending {
|
||||
background: #fff3e0;
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
&.status-approved {
|
||||
background: #e8f5e8;
|
||||
color: #388e3c;
|
||||
}
|
||||
|
||||
&.status-rejected {
|
||||
background: #ffebee;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
&.status-cancelled {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&.status-completed {
|
||||
background: #e3f2fd;
|
||||
color: #1976d2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
width: 160rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reject-section {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.reject-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #d32f2f;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.reject-content {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-footer {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
|
||||
.apply-time {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,421 @@
|
||||
<template>
|
||||
<view class="visitor-container">
|
||||
<!-- 访客申请表单 -->
|
||||
<view class="visitor-form">
|
||||
<view class="form-title">访客申请</view>
|
||||
<view class="form-item">
|
||||
<view class="label">访客姓名</view>
|
||||
<input v-model="visitorName" placeholder="请输入访客姓名" class="name-input" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">访客手机号</view>
|
||||
<input v-model="visitorPhone" placeholder="请输入访客手机号" class="phone-input" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">来访目的</view>
|
||||
<picker @change="onPurposeChange" :value="purposeIndex" :range="purposeOptions" range-key="label">
|
||||
<view class="picker">{{ purposeOptions[purposeIndex].label }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">来访时间</view>
|
||||
<picker mode="date" @change="onDateChange" :value="visitDate">
|
||||
<view class="picker">{{ visitDate || "请选择来访日期" }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">来访时段</view>
|
||||
<picker @change="onTimeChange" :value="timeIndex" :range="timeOptions" range-key="label">
|
||||
<view class="picker">{{ timeOptions[timeIndex].label }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">被访人</view>
|
||||
<input v-model="hostName" placeholder="请输入被访人姓名" class="host-input" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">备注说明</view>
|
||||
<textarea v-model="remark" placeholder="请输入备注说明(选填)" class="remark-textarea" maxlength="200" />
|
||||
<view class="char-count">{{ remark.length }}/200</view>
|
||||
</view>
|
||||
<button @click="submitVisitor" class="submit-btn" :disabled="!canSubmit">提交申请</button>
|
||||
</view>
|
||||
|
||||
<!-- 访客记录列表 -->
|
||||
<view class="visitor-list">
|
||||
<view class="list-title">访客记录</view>
|
||||
<view v-if="visitorList.length === 0" class="empty-tip"> 暂无访客记录 </view>
|
||||
<view v-else>
|
||||
<view v-for="item in visitorList" :key="item.id" class="visitor-item" @click="viewDetail(item)">
|
||||
<view class="item-header">
|
||||
<view class="visitor-info">
|
||||
<text class="visitor-name">{{ item.visitorName }}</text>
|
||||
<text class="visitor-phone">{{ item.visitorPhone }}</text>
|
||||
</view>
|
||||
<view class="status-tag" :class="getStatusClass(item.status)">{{ getStatusText(item.status) }}</view>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="purpose">来访目的:{{ getPurposeLabel(item.purpose) }}</view>
|
||||
<view class="host">被访人:{{ item.hostName }}</view>
|
||||
<view class="time">来访时间:{{ formatVisitTime(item.visitDate, item.visitTime) }}</view>
|
||||
</view>
|
||||
<view class="item-footer">
|
||||
<text class="apply-time">申请时间:{{ formatTime(item.createTime) }}</text>
|
||||
<text v-if="item.remark" class="has-remark">有备注</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from "vue"
|
||||
import meetingRoomApi from "@/sheep/api/meetingRoom"
|
||||
|
||||
const purposeOptions = [
|
||||
{ value: "1", label: "商务洽谈" },
|
||||
{ value: "2", label: "技术交流" },
|
||||
{ value: "3", label: "面试" },
|
||||
{ value: "4", label: "参观访问" },
|
||||
{ value: "5", label: "其他" },
|
||||
]
|
||||
|
||||
const timeOptions = [
|
||||
{ value: "1", label: "上午 (9:00-12:00)" },
|
||||
{ value: "2", label: "下午 (14:00-18:00)" },
|
||||
{ value: "3", label: "全天 (9:00-18:00)" },
|
||||
]
|
||||
|
||||
const visitorName = ref("")
|
||||
const visitorPhone = ref("")
|
||||
const purposeIndex = ref(0)
|
||||
const visitDate = ref("")
|
||||
const timeIndex = ref(0)
|
||||
const hostName = ref("")
|
||||
const remark = ref("")
|
||||
const visitorList = ref<any[]>([])
|
||||
|
||||
const canSubmit = computed(() => {
|
||||
return visitorName.value.trim() && visitorPhone.value.trim() && visitDate.value && hostName.value.trim()
|
||||
})
|
||||
|
||||
function onPurposeChange(e: any) {
|
||||
purposeIndex.value = e.detail.value
|
||||
}
|
||||
|
||||
function onDateChange(e: any) {
|
||||
visitDate.value = e.detail.value
|
||||
}
|
||||
|
||||
function onTimeChange(e: any) {
|
||||
timeIndex.value = e.detail.value
|
||||
}
|
||||
|
||||
function getPurposeLabel(purpose: string) {
|
||||
const option = purposeOptions.find((item) => item.value === purpose)
|
||||
return option ? option.label : "其他"
|
||||
}
|
||||
|
||||
function getStatusText(status: string) {
|
||||
const statusMap: Record<string, string> = {
|
||||
"1": "待审核",
|
||||
"2": "已通过",
|
||||
"3": "已拒绝",
|
||||
"4": "已取消",
|
||||
"5": "已完成",
|
||||
}
|
||||
return statusMap[status] || "未知"
|
||||
}
|
||||
|
||||
function getStatusClass(status: string) {
|
||||
const classMap: Record<string, string> = {
|
||||
"1": "status-pending",
|
||||
"2": "status-approved",
|
||||
"3": "status-rejected",
|
||||
"4": "status-cancelled",
|
||||
"5": "status-completed",
|
||||
}
|
||||
return classMap[status] || "status-pending"
|
||||
}
|
||||
|
||||
function formatVisitTime(date: string, time: string) {
|
||||
const timeLabel = timeOptions.find((item) => item.value === time)?.label || ""
|
||||
return `${date} ${timeLabel}`
|
||||
}
|
||||
|
||||
function formatTime(time: string) {
|
||||
if (!time) return ""
|
||||
return new Date(time).toLocaleDateString()
|
||||
}
|
||||
|
||||
async function submitVisitor() {
|
||||
if (!canSubmit.value) {
|
||||
uni.showToast({ title: "请填写完整信息", icon: "none" })
|
||||
return
|
||||
}
|
||||
|
||||
// 验证手机号格式
|
||||
const phoneRegex = /^1[3-9]\d{9}$/
|
||||
if (!phoneRegex.test(visitorPhone.value)) {
|
||||
uni.showToast({ title: "请输入正确的手机号", icon: "none" })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
uni.showLoading({ title: "提交中..." })
|
||||
|
||||
const data = {
|
||||
visitorName: visitorName.value.trim(),
|
||||
visitorPhone: visitorPhone.value.trim(),
|
||||
purpose: purposeOptions[purposeIndex.value].value,
|
||||
visitDate: visitDate.value,
|
||||
visitTime: timeOptions[timeIndex.value].value,
|
||||
hostName: hostName.value.trim(),
|
||||
remark: remark.value.trim(),
|
||||
}
|
||||
|
||||
const response: any = await meetingRoomApi.submitVisitor(data)
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
if (response.code === 0) {
|
||||
uni.showToast({ title: "提交成功", icon: "success" })
|
||||
|
||||
// 清空表单
|
||||
visitorName.value = ""
|
||||
visitorPhone.value = ""
|
||||
purposeIndex.value = 0
|
||||
visitDate.value = ""
|
||||
timeIndex.value = 0
|
||||
hostName.value = ""
|
||||
remark.value = ""
|
||||
|
||||
// 刷新列表
|
||||
loadVisitorList()
|
||||
} else {
|
||||
uni.showToast({ title: response.msg || "提交失败", icon: "none" })
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: "提交失败", icon: "none" })
|
||||
}
|
||||
}
|
||||
|
||||
function viewDetail(item: any) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/integrated/visitor/detail?id=${item.id}`,
|
||||
})
|
||||
}
|
||||
|
||||
async function loadVisitorList() {
|
||||
try {
|
||||
const response: any = await meetingRoomApi.getVisitorList({
|
||||
current: 1,
|
||||
size: 20,
|
||||
})
|
||||
|
||||
if (response.code === 0 && response.data) {
|
||||
visitorList.value = response.data.records || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("加载访客记录失败:", error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadVisitorList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.visitor-container {
|
||||
padding: 24rpx;
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.visitor-form {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.form-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.picker {
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
padding: 0 24rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.name-input,
|
||||
.phone-input,
|
||||
.host-input {
|
||||
height: 80rpx;
|
||||
padding: 0 24rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.remark-textarea {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
padding: 24rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.char-count {
|
||||
text-align: right;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
font-size: 32rpx;
|
||||
|
||||
&:disabled {
|
||||
background: #ccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
|
||||
.list-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
|
||||
.visitor-item {
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
padding: 24rpx 0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.visitor-info {
|
||||
.visitor-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.visitor-phone {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
|
||||
&.status-pending {
|
||||
background: #fff3e0;
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
&.status-approved {
|
||||
background: #e8f5e8;
|
||||
color: #388e3c;
|
||||
}
|
||||
|
||||
&.status-rejected {
|
||||
background: #ffebee;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
&.status-cancelled {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&.status-completed {
|
||||
background: #e3f2fd;
|
||||
color: #1976d2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-content {
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.purpose,
|
||||
.host,
|
||||
.time {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
margin-bottom: 8rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.item-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.apply-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.has-remark {
|
||||
font-size: 24rpx;
|
||||
color: #007aff;
|
||||
background: #e3f2fd;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +0,0 @@
|
||||
<template></template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
@@ -40,19 +40,7 @@ const moduleList = [
|
||||
launchParam: "flowStatistics",
|
||||
},
|
||||
{
|
||||
pagePath: "/pages/tabbar/invitation",
|
||||
text: "访客",
|
||||
iconPath: "/static/tabbar/invitation-select.png",
|
||||
launchParam: "invitation",
|
||||
},
|
||||
{
|
||||
pagePath: "/pages/tabbar/message",
|
||||
text: "通知公告",
|
||||
iconPath: "/static/tabbar/message-select.png",
|
||||
launchParam: "message",
|
||||
},
|
||||
{
|
||||
pagePath: "/pages/integrated/index",
|
||||
pagePath: "/pages/tabbar/integrated",
|
||||
text: "综合服务",
|
||||
iconPath: "/static/tabbar/integrated-select.png",
|
||||
launchParam: "integrated",
|
||||
@@ -87,6 +75,10 @@ const jumpByParam = async () => {
|
||||
uni.setStorageSync("launchParam", launchParam)
|
||||
uni.switchTab({ url: "/pages/tabbar/coopMerchant" })
|
||||
break
|
||||
case "integrated": // 综合服务
|
||||
uni.setStorageSync("launchParam", launchParam)
|
||||
uni.switchTab({ url: "/pages/tabbar/integrated" })
|
||||
break
|
||||
default:
|
||||
uni.hideTabBar()
|
||||
break
|
||||
@@ -99,7 +91,7 @@ const jumpByParam = async () => {
|
||||
const userRole = uni.getStorageSync("userRole") // 获取用户角色
|
||||
|
||||
if (!launchParam) return uni.hideTabBar()
|
||||
if (launchParam == "thirdApp") return uni.reLaunch({ url: "/pages/thirdApp/thirdAppList" })
|
||||
if (launchParam == "thirdApp") return uni.reLaunch({ url: "/pages/thirdApp/happyMall" })
|
||||
|
||||
switch (userRole) {
|
||||
case "merchant":
|
||||
@@ -117,6 +109,10 @@ const jumpByParam = async () => {
|
||||
uni.setStorageSync("launchParam", launchParam)
|
||||
return uni.reLaunch({ url: "/pages/tabbar/coopMerchant" })
|
||||
}
|
||||
if (launchParam === "integrated") {
|
||||
uni.setStorageSync("launchParam", launchParam)
|
||||
return uni.reLaunch({ url: "/pages/tabbar/integrated" })
|
||||
}
|
||||
case "doorAdmin":
|
||||
return uni.switchTab({ url: "/pages/tabbar/cabinet" })
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<crud-layout bg="/static/page/blue-bg.png">
|
||||
<div class="integrated-title-card">
|
||||
<div class="integrated-title">综合服务</div>
|
||||
</div>
|
||||
<div class="service-container-card">
|
||||
<div class="service-container">
|
||||
<div class="service-grid">
|
||||
<div v-for="item in services" :key="item.name" class="service-item" @click="goPage(item)">
|
||||
<image :src="item.icon" class="service-icon" />
|
||||
<div class="service-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
|
||||
const services = [
|
||||
{
|
||||
name: "todo",
|
||||
label: "工作待办",
|
||||
icon: "/src/static/icon/todo.png",
|
||||
url: "/pages/integrated/todo/index",
|
||||
},
|
||||
{
|
||||
name: "meeting-booking",
|
||||
label: "会议预定",
|
||||
icon: "/src/static/icon/meeting-booking.png",
|
||||
url: "/pages/integrated/meeting/index",
|
||||
},
|
||||
{
|
||||
name: "lost-and-found",
|
||||
label: "失物招领",
|
||||
icon: "/src/static/icon/lost-and-found.png",
|
||||
url: "/pages/integrated/lostAndFound/index",
|
||||
},
|
||||
{
|
||||
name: "feedback",
|
||||
label: "意见反馈",
|
||||
icon: "/src/static/icon/feedback.png",
|
||||
url: "/pages/integrated/feedback/index",
|
||||
},
|
||||
{
|
||||
name: "repair-report",
|
||||
label: "维修上报",
|
||||
icon: "/src/static/icon/repair-report.png",
|
||||
url: "/pages/integrated/repair/index",
|
||||
},
|
||||
{
|
||||
name: "notice",
|
||||
label: "通知公告",
|
||||
icon: "/src/static/icon/notice.png",
|
||||
url: "/pages/integrated/notice/index",
|
||||
},
|
||||
{
|
||||
name: "visitor",
|
||||
label: "访客邀约",
|
||||
icon: "/src/static/icon/visitor.png",
|
||||
url: "/pages/integrated/visitor/invitation",
|
||||
},
|
||||
]
|
||||
|
||||
onLoad(() => {
|
||||
const app = getApp()
|
||||
app._initTabbar()
|
||||
})
|
||||
|
||||
function goPage(item) {
|
||||
if (item.url) {
|
||||
uni.navigateTo({ url: item.url })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.integrated-title-card {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
margin: 32rpx 24rpx 0 24rpx;
|
||||
padding: 0 0 8rpx 0;
|
||||
}
|
||||
.integrated-title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
text-align: center;
|
||||
padding: 36rpx 0 24rpx 0;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
.service-container-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
margin: 24rpx;
|
||||
padding-bottom: 32rpx;
|
||||
}
|
||||
.service-container {
|
||||
padding: 32rpx 0 0 0;
|
||||
background: transparent;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
.service-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
.service-item {
|
||||
width: 25%;
|
||||
margin-bottom: 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.service-item:hover {
|
||||
transform: translateY(-6rpx) scale(1.06);
|
||||
box-shadow: 0 2rpx 12rpx rgba(25, 118, 210, 0.08);
|
||||
}
|
||||
.service-icon {
|
||||
width: 92rpx;
|
||||
height: 92rpx;
|
||||
margin-bottom: 14rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #f5f7fa;
|
||||
box-shadow: 0 2rpx 8rpx rgba(25, 118, 210, 0.04);
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
.service-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
body {
|
||||
background: #f5f6fa;
|
||||
}
|
||||
</style>
|
||||
@@ -1,158 +0,0 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<view class="h-20rpx" />
|
||||
<tm-sheet margin="[0]">
|
||||
<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-input color="transparent" v-model.trim="form.name" placeholder="请输入真实姓名" />
|
||||
</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-input color="transparent" v-model.trim="form.companyName" placeholder="来访公司名称" />
|
||||
</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>
|
||||
|
||||
<tm-form-item name="deptId" label="拜访部门">
|
||||
<tm-drawer :show-close="true" :show-footer="true" size="auto" title="请选择拜访部门">
|
||||
<template #trigger>
|
||||
<tm-input :modelValue="form.deptId" placeholder="请选择拜访部门" style="pointer-events: none" disabled color="transparent">
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</template>
|
||||
<tm-text color="#999999">精致美观的对话框,可全局配置风格</tm-text>
|
||||
</tm-drawer>
|
||||
</tm-form-item>
|
||||
|
||||
<tm-form-item name="reasonValue" label="拜访原因">
|
||||
<tm-picker v-model="form.reasonValue" v-model:modelStr="_pingtaistr" rangText="reasonValue" :list="visitorReasonList">
|
||||
<tm-input :modelValue="_pingtaistr" placeholder="请选择拜访原因" style="pointer-events: none" disabled color="transparent">
|
||||
<template #rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="isOtherPeople" label="是否可随行人员" direction="horizontal" labelWidth="500">
|
||||
<tm-switch v-model="form.isOtherPeople" />
|
||||
</tm-form-item>
|
||||
<tm-form-item name="isDrive" label="是否可驾驶车辆" direction="horizontal" labelWidth="500">
|
||||
<tm-switch v-model="form.isDrive" />
|
||||
</tm-form-item>
|
||||
<tm-button form-type="submit" block>提交</tm-button>
|
||||
</tm-form>
|
||||
</tm-sheet>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { reactive, ref } from "vue"
|
||||
import { invitationApi } from "@/sheep/api/index"
|
||||
|
||||
const form = reactive({
|
||||
name: "",
|
||||
mobile: "",
|
||||
companyName: "",
|
||||
vStartDate: "",
|
||||
vEndDate: "",
|
||||
deptId: "",
|
||||
reasonValue: "",
|
||||
isOtherPeople: true,
|
||||
isDrive: true,
|
||||
})
|
||||
const _timestr = ref("2025-01-01")
|
||||
const _pingtaistr = ref("")
|
||||
const visitorReasonList = ref([])
|
||||
|
||||
onLoad(() => {
|
||||
const app = getApp()
|
||||
app._initTabbar()
|
||||
|
||||
getVisitorReasonList()
|
||||
})
|
||||
|
||||
// 获取拜访原因列表
|
||||
const getVisitorReasonList = async () => {
|
||||
const { code, data } = await invitationApi.getDictListByType("1")
|
||||
if (code == 0 && data) {
|
||||
visitorReasonList.value = data
|
||||
return
|
||||
}
|
||||
visitorReasonList.value = []
|
||||
}
|
||||
|
||||
const rules: Record<string, TM.FORM_RULE[]> = {
|
||||
name: [{ required: true, message: "姓名2到5个字符", rule: /^.{2,5}$/ }],
|
||||
mobile: [{ required: true, message: "请输入正确的手机号", rule: /^(?:(?:\+|00)86)?1\d{10}$/ }],
|
||||
companyName: [{ required: true, message: "请输入来访公司名称" }],
|
||||
vStartDate: [{ required: true, message: "请选择拜访开始时间" }],
|
||||
vEndDate: [{ required: true, message: "请选择拜访离开时间" }],
|
||||
deptId: [{ required: true, message: "请选择拜访部门" }],
|
||||
reasonValue: [{ required: true, message: "请选择拜访原因" }],
|
||||
}
|
||||
|
||||
// 表单提交
|
||||
const onsubmit = (evt: TM.FORM_SUBMIT_RESULT) => {
|
||||
console.log("校验结果:", evt)
|
||||
if (!evt.isPass) return
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "确认提交吗?",
|
||||
confirmColor: "#0094ff",
|
||||
success: async () => {
|
||||
try {
|
||||
const { code, data, msg } = await invitationApi.saveInvitation(form)
|
||||
if (code == 0) {
|
||||
uni.showToast({ title: "提交成功", icon: "success" })
|
||||
uni.switchTab({ url: "/pages/tabbar/invitationRecord" })
|
||||
return
|
||||
}
|
||||
uni.showToast({ title: msg || "提交失败", icon: "none" })
|
||||
} catch (error: any) {
|
||||
uni.showToast({ title: error.message || "提交失败", icon: "none" })
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -1,137 +0,0 @@
|
||||
<template>
|
||||
<crud-layout bg="/static/page/blue-bg.png">
|
||||
<z-paging
|
||||
ref="pageRef"
|
||||
height="100%"
|
||||
:fixed="false"
|
||||
v-model="pageHook.list"
|
||||
:default-page-size="10"
|
||||
safe-area-inset-bottom
|
||||
:loading-more-enabled="true"
|
||||
:show-scrollbar="false"
|
||||
@query="query"
|
||||
:auto-clean-list-when-reload="false"
|
||||
:auto-scroll-to-top-when-reload="false"
|
||||
@scroll="pageHook.scroll"
|
||||
>
|
||||
<template #top>
|
||||
<view class="bg-white rounded-20rpx my-20rpx font-bold">
|
||||
<z-tabs
|
||||
:list="[
|
||||
{ name: '审核申请', value: 0 },
|
||||
{ name: '我的邀约', value: 1 },
|
||||
]"
|
||||
bar-width="150"
|
||||
:active-style="{
|
||||
fontSize: '30rpx',
|
||||
}"
|
||||
:inactive-style="{
|
||||
fontSize: '30rpx',
|
||||
}"
|
||||
@change="onTabChange"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="bg-white rounded-16rpx px-20rpx">
|
||||
<z-tabs :list="tabs" @change="doTab" />
|
||||
</view>
|
||||
<view class="h-20rpx" />
|
||||
</template>
|
||||
<view class="flex-col justify-center card p-3 m-b-20rpx" v-for="(v, i) in pageHook.list" :key="i">
|
||||
<view class="flex justify-between items-center">
|
||||
<view v-if="v.checkStatus == '1'" class="p-1 c-white! sub-text-26 rounded-1 bg-#f5a623">待审核</view>
|
||||
<view v-if="v.checkStatus == '2'" class="p-1 c-white! sub-text-26 rounded-1 bg-#2db7f5">待到访</view>
|
||||
<view v-if="v.checkStatus == '3'" class="p-1 c-white! sub-text-26 rounded-1 bg-#52c41a">已拒绝</view>
|
||||
<view v-if="v.checkStatus == '5'" class="p-1 c-white! sub-text-26 rounded-1 bg-#f5222d">已取消</view>
|
||||
<view class="sub-text-26">{{ v.vstartDate + " ~ " + v.vendDate }}</view>
|
||||
</view>
|
||||
<view class="flex justify-center items-center my-2">
|
||||
<view class="sub-text-32 mr-4 flex-col-center font-italic bg-#ECF4F9 p-4 rounded-lt-12 rounded-rb-12">
|
||||
<view>{{ v.name }}</view>
|
||||
<view>{{ v.mobile }}</view>
|
||||
</view>
|
||||
<view class="second-text-28 bg-#ECF4F9 p-4 rounded-lb-12 rounded-rt-12">接待人:{{ v.userName }}</view>
|
||||
</view>
|
||||
<view v-if="v.checkStatus == 1" class="flex justify-end 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>
|
||||
</z-paging>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from "vue"
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app"
|
||||
import { hooks } from "@@/index"
|
||||
import zTabs from "@@/components/other/z-tabs/z-tabs.vue"
|
||||
import { invitationApi } from "@/sheep/api"
|
||||
|
||||
const tabs = [
|
||||
{ value: "", name: "全部", disabled: false },
|
||||
{ value: "1", name: "待审核", disabled: false },
|
||||
{ value: "2", name: "待到访", disabled: false },
|
||||
{ value: "3", name: "已拒绝", disabled: false },
|
||||
{ value: "5", name: "已取消", disabled: false },
|
||||
]
|
||||
|
||||
const state = reactive<any>({
|
||||
params: {
|
||||
checkStatus: tabs[0].value,
|
||||
type: 1, // type为1:主动申请 type为2:邀约 type为3:后台创建
|
||||
},
|
||||
})
|
||||
const pageRef = ref<any>(null)
|
||||
|
||||
onLoad(() => {
|
||||
const app = getApp()
|
||||
app._initTabbar()
|
||||
})
|
||||
|
||||
onShow(async () => {
|
||||
const isLogin = await hooks.useCheckLogin()
|
||||
if (!isLogin) return
|
||||
if (pageRef.value) {
|
||||
pageRef.value.reload(false)
|
||||
}
|
||||
})
|
||||
|
||||
const doTab = (e: any) => {
|
||||
const target = tabs[e]
|
||||
state.params.checkStatus = target.value
|
||||
pageRef.value.reload(true)
|
||||
}
|
||||
|
||||
const pageHook = hooks.usePage(pageRef, invitationApi.getInvitationRecord, null, "data.records")
|
||||
|
||||
const query = (page: number, pagesize: number) => {
|
||||
const isLogin = hooks.useStaticCheckLogin()
|
||||
if (!isLogin) return
|
||||
pageHook.startPage(page, pagesize, false, state.params)
|
||||
}
|
||||
|
||||
const onTabChange = (e: number) => {
|
||||
state.params.type = e + 1
|
||||
pageRef.value.reload(false)
|
||||
}
|
||||
|
||||
// 审核通过/拒绝
|
||||
const visitorCheck = async (id: string, checkStatus: number | string) => {
|
||||
try {
|
||||
const { code, msg } = await invitationApi.updateCheckStatus({ id, checkStatus })
|
||||
if (code == 0) {
|
||||
uni.showToast({ title: "操作成功", icon: "success" })
|
||||
pageRef.value.reload(false)
|
||||
return
|
||||
}
|
||||
uni.showToast({ title: msg, icon: "error" })
|
||||
} catch (error: any) {
|
||||
uni.showToast({ title: error.message, icon: "error" })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -405,10 +405,9 @@ const initMenuConfig = () => {
|
||||
{ text: "dev-外来人员就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/foreign-list" },
|
||||
{ text: "dev-跨单位就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/stride-list" },
|
||||
{ text: "dev-包厢预定", icon: "/static/icon/service_4.png", router: "/pages/boxes/apply-list" },
|
||||
{ text: "dev-第三方应用", icon: "/static/icon/login.png", router: "/pages/thirdApp/thirdAppList" },
|
||||
{ text: "dev-第三方应用", icon: "/static/icon/login.png", router: "/pages/thirdApp/happyMall" },
|
||||
{ text: "dev-登录", icon: "/static/icon/login.png", router: "/pages/user/login" },
|
||||
{ text: "dev-角色选择", icon: "/static/icon/login.png", router: "/pages/user/userRole" },
|
||||
{ text: "dev-意见反馈", icon: "/static/icon/service_5.png", router: "/pages/integrated/feedback/index" },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<crud-layout bg="/static/page/blue-bg.png">
|
||||
<z-paging
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<!-- <z-paging
|
||||
ref="pageRef"
|
||||
height="100%"
|
||||
:fixed="false"
|
||||
@@ -13,8 +13,8 @@
|
||||
:auto-clean-list-when-reload="false"
|
||||
:auto-scroll-to-top-when-reload="false"
|
||||
@scroll="pageHook.scroll"
|
||||
>
|
||||
<view class="h-20rpx" />
|
||||
> -->
|
||||
<!-- <view class="h-20rpx" />
|
||||
<view class="flex-row card p-20rpx m-b-20rpx" v-for="(v, i) in pageHook.list" :key="i" @tap="toDetail(v)">
|
||||
<image class="w-180rpx h-180rpx rounded-12rpx my-auto" :src="filePath(v.logoPic)" mode="aspectFill" />
|
||||
<view class="flex-1 flex-col m-l-20rpx">
|
||||
@@ -38,59 +38,71 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<tm-modal
|
||||
v-model:show="state.show"
|
||||
:overlayClick="false"
|
||||
title="注意"
|
||||
position="bottom"
|
||||
height="auto"
|
||||
:showCancel="true"
|
||||
cancelText="取消"
|
||||
confirmText="继续访问"
|
||||
@confirm="thirdAppJump()"
|
||||
@cancel="state.show = false"
|
||||
>
|
||||
<tm-text font-size="30" color="#999999">
|
||||
您即将离开智慧后勤应用并跳转到第三方应用,请注意个人隐私和账号财产安全。您通过第三方服务页面主动提供给第三方商户的个人信息,相关信息保护责任由第三方商户承担。
|
||||
</tm-text>
|
||||
</tm-modal>
|
||||
</z-paging>
|
||||
<view class="w-full text-center mt-20">
|
||||
<image src="@/static/page/happyMall.png" class="rounded-full w-1/3" mode="widthFix" />
|
||||
<view>美好商城</view>
|
||||
<view class="h-100rpx" />
|
||||
<tm-button @click="show = true">继续跳转</tm-button>
|
||||
</view>
|
||||
|
||||
<tm-modal
|
||||
v-model:show="show"
|
||||
:overlayClick="false"
|
||||
title="注意"
|
||||
position="bottom"
|
||||
height="auto"
|
||||
:showCancel="true"
|
||||
cancelText="取消"
|
||||
confirmText="继续访问"
|
||||
@confirm="thirdAppJump()"
|
||||
@cancel="show = false"
|
||||
>
|
||||
<tm-text font-size="30" color="#999999">
|
||||
您即将离开智慧后勤应用并跳转到第三方应用,请注意个人隐私和账号财产安全。您通过第三方服务页面主动提供给第三方商户的个人信息,相关信息保护责任由第三方商户承担。
|
||||
</tm-text>
|
||||
</tm-modal>
|
||||
<!-- </z-paging> -->
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from "vue"
|
||||
import { onShow } from "@dcloudio/uni-app"
|
||||
import { ref } from "vue"
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { hooks } from "@@/index"
|
||||
import { merchantApi } from "@@/api"
|
||||
import { filePath } from "@@/utils/file"
|
||||
// import { filePath } from "@@/utils/file"
|
||||
|
||||
const params = reactive<any>({
|
||||
type: "4",
|
||||
})
|
||||
// const params = reactive<any>({
|
||||
// type: "4",
|
||||
// })
|
||||
|
||||
const state = reactive({
|
||||
show: false,
|
||||
})
|
||||
const show = ref(false)
|
||||
|
||||
onShow(() => {
|
||||
if (pageRef.value) pageRef.value.reload(false)
|
||||
})
|
||||
|
||||
const pageRef = ref<any>(null)
|
||||
const pageHook = hooks.usePage(pageRef, merchantApi.page, null, "data.records")
|
||||
const query = (page: number, pagesize: number) => {
|
||||
pageHook.startPage(page, pagesize, false, params)
|
||||
}
|
||||
|
||||
const toDetail = async (v: any) => {
|
||||
onLoad(async () => {
|
||||
const isLogin = await hooks.useCheckLogin()
|
||||
if (isLogin) {
|
||||
state.show = true
|
||||
show.value = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// onShow(() => {
|
||||
// if (pageRef.value) pageRef.value.reload(false)
|
||||
// })
|
||||
|
||||
// const pageRef = ref<any>(null)
|
||||
// const pageHook = hooks.usePage(pageRef, merchantApi.page, null, "data.records")
|
||||
// const query = (page: number, pagesize: number) => {
|
||||
// pageHook.startPage(page, pagesize, false, params)
|
||||
// }
|
||||
|
||||
// const toDetail = async (v: any) => {
|
||||
// const isLogin = await hooks.useCheckLogin()
|
||||
// if (isLogin) {
|
||||
// state.show = true
|
||||
// }
|
||||
// }
|
||||
|
||||
// 确认或拒绝协议
|
||||
const thirdAppJump = async () => {
|
||||
@@ -98,7 +110,7 @@ const thirdAppJump = async () => {
|
||||
uni.showLoading({ title: "跳转中", mask: true })
|
||||
const res: any = await merchantApi.lbLogin()
|
||||
if (res.code == 0) {
|
||||
state.show = false
|
||||
show.value = false
|
||||
if (!res.data) return uni.showToast({ title: "url错误", icon: "none" })
|
||||
window.location.href = res.data
|
||||
}
|
||||
@@ -4,7 +4,7 @@ export default {
|
||||
// 获取拜访原因字典
|
||||
getDictListByType: (type: number | string): Promise<any> =>
|
||||
request({
|
||||
url: `/integrated/app/visitor/getDictListByType?type=${type}`,
|
||||
url: `/integrated/visitors/getDictListByType?type=${type}`,
|
||||
method: "GET",
|
||||
}),
|
||||
|
||||
@@ -32,4 +32,11 @@ export default {
|
||||
method: "GET",
|
||||
params,
|
||||
}),
|
||||
|
||||
// 查看邀约详情
|
||||
getInvitationDetail: (id: string): Promise<any> =>
|
||||
request({
|
||||
url: `/integrated/visitors/${id}`,
|
||||
method: "GET",
|
||||
}),
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<view class="date-time-range">
|
||||
<view
|
||||
class="date-range-item"
|
||||
:class="[selectedDateIndex == index ? 'active' : '']"
|
||||
v-for="(item, index) in getLast7DaysWeek()"
|
||||
:key="index"
|
||||
@click="selectDate(item, index)"
|
||||
>
|
||||
<view>{{ item.week }}</view>
|
||||
<view>{{ item.date }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="day-time-point-list">
|
||||
<button
|
||||
:class="selectedTimePointIndex == index ? 'active' : ''"
|
||||
v-for="(item, index) in dayTimePointList"
|
||||
:key="index"
|
||||
@click="selectTimePoint(item, index)"
|
||||
>
|
||||
{{ item.slice(0, 5) }}
|
||||
</button>
|
||||
</view>
|
||||
<view class="px-20rpx py-2 flex justify-between">
|
||||
<tm-button skin="thin" block @click="emit('cancel')">取消</tm-button>
|
||||
|
||||
<tm-button block @click="onConfirm">确定</tm-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue"
|
||||
|
||||
const emit = defineEmits(["confirm", "cancel"])
|
||||
|
||||
// 未来一周的日期
|
||||
const getLast7DaysWeek = (): { week: string; date: string; fullDate: string }[] => {
|
||||
const weekMap: string[] = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]
|
||||
const result: { week: string; date: string; fullDate: string }[] = []
|
||||
const today = new Date()
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const d = new Date(today)
|
||||
d.setDate(today.getDate() + i)
|
||||
const week = weekMap[d.getDay()]
|
||||
const month = (d.getMonth() + 1).toString().padStart(2, "0")
|
||||
const day = d.getDate().toString().padStart(2, "0")
|
||||
const fullDate = `${d.getFullYear()}-${month}-${day}`
|
||||
result.push({
|
||||
week,
|
||||
date: `${month}-${day}`,
|
||||
fullDate,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 生成时间点列表(8:00-18:00,每半小时一个时间点)
|
||||
const dayTimePointList = computed(() => {
|
||||
const times: string[] = []
|
||||
for (let i = 8; i < 18; i++) {
|
||||
times.push(`${i.toString().padStart(2, "0")}:00:00`)
|
||||
times.push(`${i.toString().padStart(2, "0")}:30:00`)
|
||||
}
|
||||
times.push("18:00:00") // 添加18:00作为结束时间
|
||||
return times
|
||||
})
|
||||
|
||||
const selectedDateIndex = ref(0)
|
||||
const selectedDateValue = ref<{ week: string; date: string; fullDate: string }>(getLast7DaysWeek()[0])
|
||||
const selectedTimePointIndex = ref(0)
|
||||
const selectedTimePointValue = ref(dayTimePointList.value[0])
|
||||
|
||||
// 选择周
|
||||
const selectDate = (value: { week: string; date: string; fullDate: string }, index: number) => {
|
||||
selectedDateValue.value = value
|
||||
selectedDateIndex.value = index
|
||||
}
|
||||
|
||||
// 选择时间点
|
||||
const selectTimePoint = (value: string, index: number) => {
|
||||
selectedTimePointValue.value = value
|
||||
selectedTimePointIndex.value = index
|
||||
}
|
||||
|
||||
// 确认
|
||||
function onConfirm() {
|
||||
emit("confirm", selectedDateValue.value, selectedTimePointValue.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.date-time-range {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.date-time-range .date-range-item {
|
||||
padding: 10rpx;
|
||||
background-color: #eee;
|
||||
border-radius: 12rpx;
|
||||
margin: 0 10rpx;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.date-time-range .active {
|
||||
background: #0779fd;
|
||||
border-color: #4988f2;
|
||||
color: white;
|
||||
}
|
||||
.day-time-point-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.day-time-point-list button {
|
||||
flex: 1 0 90rpx;
|
||||
padding: 6rpx 0;
|
||||
border: none;
|
||||
background: #eeeeee;
|
||||
border-radius: 12rpx;
|
||||
font-size: 26rpx;
|
||||
cursor: pointer;
|
||||
}
|
||||
.day-time-point-list .active {
|
||||
background-color: #0779fd;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
@@ -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>
|
||||
|
After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 699 B |
|
After Width: | Height: | Size: 677 B |