From 25df88d0ab9d7444ed1d162b9efa1259ac2f6f66 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期一, 27 四月 2026 10:13:53 +0800
Subject: [PATCH] 小程序对接finish
---
yuyueMiniProgram/src/pages/index/index.vue | 660 +++++++++++++++++++++++++++++++++++++++--------------------
1 files changed, 435 insertions(+), 225 deletions(-)
diff --git a/yuyueMiniProgram/src/pages/index/index.vue b/yuyueMiniProgram/src/pages/index/index.vue
index d90c754..1c21593 100644
--- a/yuyueMiniProgram/src/pages/index/index.vue
+++ b/yuyueMiniProgram/src/pages/index/index.vue
@@ -1,150 +1,369 @@
<script setup lang="ts">
-import { ref } from 'vue'
+import QRCode from 'qrcode'
+import { fetchMyApprovalTodos, fetchMyVisitorCodes, fetchVisitRecords } from '@/api/appointment'
+import { useMiniAuthStore } from '@/store/auth'
+import type { MiniMyVisitorCodeOrderItem, MiniMyVisitorCodeTicketItem } from '@/types/appointment'
-const { safeAreaInsets } = uni.getSystemInfoSync()
+interface VisitorCodeDisplayTicket extends MiniMyVisitorCodeTicketItem {
+ qrCode: string
+}
-// 弹窗显示状态
-const showLoginPopup = ref(false)
+interface VisitorCodeDisplayOrder extends Omit<MiniMyVisitorCodeOrderItem, 'qrCodes'> {
+ timeText: string
+ qrCodes: VisitorCodeDisplayTicket[]
+}
-// 登录状态
-const isLoggedIn = ref(true)
-
-// 用户角色: 'employee' 员工 | 'visitor' 普通访客
-const userRole = ref<'employee' | 'visitor'>('visitor')
-
-// 访客码数据
-const visitorList = ref([
- {
- id: 1,
- name: '王敏',
- department: '管理部',
- area: 'AAAAA',
- time: '2023-10-10 11:30至2023-10-15 18:30',
- qrCode: '/static/home/成都喜望科技有限公司.png',
- visitorName: '王琦',
- },
- {
- id: 2,
- name: '李华',
- department: '技术部',
- area: 'BBBBB',
- time: '2023-11-01 09:00至2023-11-01 18:00',
- qrCode: '/static/home/成都喜望科技有限公司.png',
- visitorName: '张三',
- },
-])
-const currentVisitorIndex = ref(0)
+const systemInfo = uni.getSystemInfoSync()
+const { safeAreaInsets } = systemInfo
+const parkFlowBottomPx = Math.max(24, Math.round(systemInfo.windowHeight / 8))
const router = useRouter()
+const authStore = useMiniAuthStore()
+const visitorOrders = ref<VisitorCodeDisplayOrder[]>([])
+const currentOrderIndex = ref(0)
+const loadingVisitorCodes = ref(false)
+const qrRenderToken = ref(0)
+const ticketIndexMap = ref<Record<number, number>>({})
+const approvalTodoCount = ref(0)
+const visitRecordCount = ref(0)
-// 切换访客码
-function handlePrevVisitor() {
- if (currentVisitorIndex.value > 0) {
- currentVisitorIndex.value--
+const profile = computed(() => authStore.profile)
+const isEmployee = computed(() => profile.value?.userType === 'EMPLOYEE')
+const userRoleText = computed(() => {
+ if (!profile.value) {
+ return ''
+ }
+ if (profile.value.userType === 'EMPLOYEE') {
+ return profile.value.deptLeaderFlag ? '部门领导' : '员工'
+ }
+ return '普通访客'
+})
+const secondaryText = computed(() => {
+ if (!profile.value) {
+ return ''
+ }
+ if (profile.value.userType === 'EMPLOYEE') {
+ return profile.value.deptName || '未分配部门'
+ }
+ return profile.value.companyName || ''
+})
+const currentOrder = computed(() => visitorOrders.value[currentOrderIndex.value] || null)
+const currentTicket = computed(() => {
+ if (!currentOrder.value) {
+ return null
+ }
+ const index = ticketIndexMap.value[currentOrder.value.orderId] ?? 0
+ return currentOrder.value.qrCodes[index] || currentOrder.value.qrCodes[0] || null
+})
+
+function handlePrevOrder() {
+ if (currentOrderIndex.value > 0) {
+ currentOrderIndex.value--
}
}
-function handleNextVisitor() {
- if (currentVisitorIndex.value < visitorList.value.length - 1) {
- currentVisitorIndex.value++
+function handleNextOrder() {
+ if (currentOrderIndex.value < visitorOrders.value.length - 1) {
+ currentOrderIndex.value++
}
}
-function onSwiperChange(e: any) {
- currentVisitorIndex.value = e.detail.current
+function onSwiperChange(e: { detail?: { current?: number } }) {
+ currentOrderIndex.value = e.detail?.current || 0
}
-// 下载访客码
-function handleDownloadVisitorCode(qrCodeUrl: string) {
- // 小程序环境中获取图片信息并保存到相册
- uni.showLoading({ title: '保存中...' })
+function currentTicketIndex(orderId: number) {
+ return ticketIndexMap.value[orderId] ?? 0
+}
- // 这里如果是本地路径如 /static/xxx,可以直接用。如果后续换成网络图,需要先 uni.downloadFile
- // 为了演示,统一走 uni.getImageInfo 以校验文件存在并保存
- uni.getImageInfo({
- src: qrCodeUrl,
- success: (image) => {
- uni.saveImageToPhotosAlbum({
- filePath: image.path,
- success: () => {
- uni.hideLoading()
- uni.showToast({
- title: '保存成功',
- icon: 'success',
- })
- },
- fail: (err) => {
- uni.hideLoading()
- // 拒绝授权后会走 fail
- if (err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') {
- uni.showModal({
- title: '提示',
- content: '需要您授权保存相册',
- success(res) {
- if (res.confirm) {
- uni.openSetting({
- success(settingdata) {
- if (settingdata.authSetting['scope.writePhotosAlbum']) {
- uni.showToast({ title: '授权成功,请重新点击下载', icon: 'none' })
- }
- else {
- uni.showToast({ title: '授权失败,无法保存', icon: 'none' })
- }
- },
- })
- }
- },
- })
- }
- else {
- uni.showToast({
- title: '保存失败',
- icon: 'none',
- })
- }
- },
+function handlePrevTicket(orderId: number) {
+ const currentIndex = currentTicketIndex(orderId)
+ if (currentIndex <= 0) {
+ return
+ }
+ ticketIndexMap.value = {
+ ...ticketIndexMap.value,
+ [orderId]: currentIndex - 1,
+ }
+}
+
+function handleNextTicket(orderId: number) {
+ const order = visitorOrders.value.find(item => item.orderId === orderId)
+ const currentIndex = currentTicketIndex(orderId)
+ if (!order || currentIndex >= order.qrCodes.length - 1) {
+ return
+ }
+ ticketIndexMap.value = {
+ ...ticketIndexMap.value,
+ [orderId]: currentIndex + 1,
+ }
+}
+
+function formatVisitTime(order: MiniMyVisitorCodeOrderItem) {
+ return order.visitTimeText || '暂无有效期'
+}
+
+function formatDateTime(value?: string | null) {
+ if (!value) {
+ return ''
+ }
+ return value.replace('T', ' ').slice(0, 16)
+}
+
+function buildVisitorOrders(orders: MiniMyVisitorCodeOrderItem[]): VisitorCodeDisplayOrder[] {
+ return orders.map(order => ({
+ ...order,
+ timeText: formatVisitTime(order),
+ qrCodes: order.qrCodes.map((ticket: MiniMyVisitorCodeTicketItem) => ({
+ ...ticket,
+ qrCode: '',
+ })),
+ }))
+}
+
+async function loadVisitorCodes() {
+ loadingVisitorCodes.value = true
+ const currentToken = qrRenderToken.value + 1
+ qrRenderToken.value = currentToken
+ try {
+ const orders = await fetchMyVisitorCodes()
+ const displayOrders = buildVisitorOrders(orders)
+ visitorOrders.value = displayOrders
+ currentOrderIndex.value = 0
+ ticketIndexMap.value = Object.fromEntries(displayOrders.map(item => [item.orderId, 0]))
+ await nextTick()
+ const hydratedOrders = await Promise.all(displayOrders.map(async order => ({
+ ...order,
+ qrCodes: await Promise.all(order.qrCodes.map(async ticket => ({
+ ...ticket,
+ qrCode: await createQrImage(ticket.payload),
+ }))),
+ })))
+ if (qrRenderToken.value === currentToken) {
+ visitorOrders.value = hydratedOrders
+ }
+ }
+ catch (error) {
+ visitorOrders.value = []
+ ticketIndexMap.value = {}
+ uni.showToast({
+ title: error instanceof Error ? error.message : '加载访客码失败',
+ icon: 'none',
+ })
+ }
+ finally {
+ if (qrRenderToken.value === currentToken) {
+ loadingVisitorCodes.value = false
+ }
+ }
+}
+
+async function loadEmployeeDashboardCounts() {
+ if (!isEmployee.value) {
+ approvalTodoCount.value = 0
+ visitRecordCount.value = 0
+ return
+ }
+ try {
+ const [todoResult, visitRecordResult] = await Promise.all([
+ fetchMyApprovalTodos({ page: 1, size: 1 }),
+ fetchVisitRecords({ page: 1, size: 1, futureOnly: true }),
+ ])
+ approvalTodoCount.value = todoResult.total
+ visitRecordCount.value = visitRecordResult.total
+ }
+ catch (error) {
+ approvalTodoCount.value = 0
+ visitRecordCount.value = 0
+ console.warn('load employee dashboard counts failed', error)
+ }
+}
+
+async function createQrImage(payload: string) {
+ // #ifdef H5
+ return QRCode.toDataURL(payload, {
+ errorCorrectionLevel: 'M',
+ margin: 1,
+ width: 300,
+ })
+ // #endif
+
+ const qr = QRCode.create(payload, {
+ errorCorrectionLevel: 'M',
+ margin: 1,
+ })
+ const size = 300
+ const moduleCount = qr.modules.size
+ const tileSize = size / moduleCount
+ const canvasId = 'visitorQrCanvas'
+
+ return new Promise<string>((resolve, reject) => {
+ const context = uni.createCanvasContext(canvasId)
+ context.setFillStyle('#FFFFFF')
+ context.fillRect(0, 0, size, size)
+ context.setFillStyle('#000000')
+
+ for (let row = 0; row < moduleCount; row += 1) {
+ for (let col = 0; col < moduleCount; col += 1) {
+ const index = row * moduleCount + col
+ if (!qr.modules.data[index]) {
+ continue
+ }
+ const x = Math.round(col * tileSize)
+ const y = Math.round(row * tileSize)
+ const width = Math.ceil((col + 1) * tileSize) - x
+ const height = Math.ceil((row + 1) * tileSize) - y
+ context.fillRect(x, y, width, height)
+ }
+ }
+
+ context.draw(false, () => {
+ uni.canvasToTempFilePath({
+ canvasId,
+ width: size,
+ height: size,
+ destWidth: size * 2,
+ destHeight: size * 2,
+ success: res => resolve(res.tempFilePath),
+ fail: reject,
})
- },
- fail: () => {
+ })
+ })
+}
+
+function handleDownloadVisitorCode(qrCodeUrl: string) {
+ if (!qrCodeUrl) {
+ uni.showToast({
+ title: '访客码生成中',
+ icon: 'none',
+ })
+ return
+ }
+
+ uni.showLoading({ title: '保存中...' })
+ void resolveSaveFilePath(qrCodeUrl)
+ .then(filePath => saveImageToAlbum(filePath))
+ .catch((error) => {
uni.hideLoading()
uni.showToast({
- title: '获取图片失败',
+ title: error instanceof Error ? error.message : '保存失败',
icon: 'none',
})
+ })
+}
+
+function resolveSaveFilePath(src: string) {
+ return new Promise<string>((resolve, reject) => {
+ if (!src) {
+ reject(new Error('访客码生成中'))
+ return
+ }
+
+ if (src.startsWith('http://') || src.startsWith('https://')) {
+ uni.downloadFile({
+ url: src,
+ success: (res) => {
+ if (res.statusCode === 200 && res.tempFilePath) {
+ resolve(res.tempFilePath)
+ return
+ }
+ reject(new Error('下载图片失败'))
+ },
+ fail: () => reject(new Error('下载图片失败')),
+ })
+ return
+ }
+
+ if (src.startsWith('wxfile://') || src.startsWith('file://') || src.startsWith('/')) {
+ resolve(src)
+ return
+ }
+
+ uni.getImageInfo({
+ src,
+ success: image => resolve(image.path),
+ fail: () => reject(new Error('获取图片失败')),
+ })
+ })
+}
+
+function saveImageToAlbum(filePath: string) {
+ uni.saveImageToPhotosAlbum({
+ filePath,
+ success: () => {
+ uni.hideLoading()
+ uni.showToast({
+ title: '保存成功',
+ icon: 'success',
+ })
+ },
+ fail: (err) => {
+ uni.hideLoading()
+ const errMsg = err?.errMsg || ''
+ if (errMsg.includes('auth deny') || errMsg.includes('authorize no response')) {
+ uni.showModal({
+ title: '提示',
+ content: '需要您授权保存相册',
+ success(res) {
+ if (res.confirm) {
+ uni.openSetting({
+ success(settingdata) {
+ if (settingdata.authSetting['scope.writePhotosAlbum']) {
+ uni.showToast({ title: '授权成功,请重新点击下载', icon: 'none' })
+ }
+ else {
+ uni.showToast({ title: '授权失败,无法保存', icon: 'none' })
+ }
+ },
+ })
+ }
+ },
+ })
+ return
+ }
+ uni.showToast({
+ title: '保存失败',
+ icon: 'none',
+ })
+ console.error('save visitor code failed', err)
},
})
}
-// 触发弹窗显示
-function handleShowLoginPopup() {
- showLoginPopup.value = true
+function handleOpenParkFlow() {
+ uni.navigateTo({
+ url: `/pages/aboutUs/index?title=${encodeURIComponent('入园流程')}&code=CONTENT_PARK_FLOW_HTML`,
+ })
}
-// 取消弹窗
-function handleCancelLogin() {
- showLoginPopup.value = false
+function handleOpenApprovalTodo() {
+ uni.navigateTo({ url: '/pages/reviewList/index' })
}
-// 确认登录(获取手机号回调)
-function handleConfirmLogin(e: any) {
- showLoginPopup.value = false
- if (e.detail.errMsg === 'getPhoneNumber:ok') {
- // 用户同意授权,这里可以拿到 e.detail.code,发送给后端换取手机号
- console.log('获取手机号成功:', e.detail)
- isLoggedIn.value = true // 模拟登录成功状态
- uni.showToast({
- title: '登录成功',
- icon: 'success',
- })
- }
- else {
- // 用户拒绝授权
- console.log('用户拒绝授权手机号:', e.detail.errMsg)
- uni.showToast({
- title: '已取消登录',
- icon: 'none',
- })
- }
+function handleOpenVisitRecords() {
+ uni.navigateTo({
+ url: '/pages/reception/index',
+ })
}
+
+function buildVisitorCodeSharePath(orderUuid?: string | null) {
+ const uuid = orderUuid || currentOrder.value?.orderUuid
+ return uuid ? `/pages/visitorCode/index?uuid=${encodeURIComponent(uuid)}` : '/pages/index/index'
+}
+
+onShareAppMessage((res) => {
+ const orderUuid = res?.target?.dataset?.orderUuid as string | undefined
+ const visitedUserDisplay = res?.target?.dataset?.visitedUserDisplay as string | undefined
+ return {
+ title: visitedUserDisplay ? `${visitedUserDisplay}的访客码` : '访客码分享',
+ path: buildVisitorCodeSharePath(orderUuid),
+ }
+})
+
+onShow(() => {
+ authStore.restoreSession()
+ void loadVisitorCodes()
+ void loadEmployeeDashboardCounts()
+})
definePage({
name: 'home',
@@ -157,48 +376,39 @@
<template>
<view class="relative">
- <!-- 背景图 -->
<image src="/static/home/bg.png" class="absolute left-0 top-0 z-0 h-[1624rpx] w-full" mode="scaleToFill" />
- <!-- 内容区 -->
<view class="relative z-4" :style="{ paddingTop: `${safeAreaInsets?.top || 44}px` }">
- <!-- 导航栏标题 -->
<view class="mb-[64rpx] h-[88rpx] flex items-center px-[32rpx]">
<text class="text-[36rpx] text-white font-medium">
预约小程序
</text>
</view>
- <!-- 欢迎使用区 -->
- <view class="mb-[36rpx] flex items-center pl-[26rpx] pr-[22rpx]" @click="!isLoggedIn && handleShowLoginPopup()">
+ <view class="mb-[36rpx] flex items-center pl-[26rpx] pr-[22rpx]">
<image src="/static/home/hi.png" class="mr-[24rpx] h-[96rpx] w-[212rpx]" mode="aspectFit" />
- <view class="welcome-title flex items-center text-[56rpx] text-white font-[600]">
- <template v-if="!isLoggedIn">
- <text>欢迎使用,</text>
- <view class="login-text relative">
- 请登录
- <view class="login-text__underline absolute bottom-0 left-0 h-[4rpx] w-full bg-white" />
- </view>
- </template>
- <template v-else>
- <text>王丽</text>
- </template>
+ <view class="flex flex-col">
+ <view class="welcome-title flex items-center text-[56rpx] text-white font-[600]">
+ <text>{{ profile?.displayName || '用户' }}</text>
+ </view>
+ <text class="mt-[8rpx] text-[24rpx] text-white/90">
+ {{ userRoleText }}<template v-if="secondaryText"> · {{ secondaryText }}</template>
+ </text>
</view>
</view>
- <!-- 待办/待接待区 -->
- <view v-if="userRole === 'employee'" class="mb-[32rpx] w-full pl-[26rpx] pr-[22rpx]" style="box-sizing: border-box;">
+ <view v-if="isEmployee" class="mb-[32rpx] w-full pl-[26rpx] pr-[22rpx]" style="box-sizing: border-box;">
<view
class="rounded-[16rpx] p-[24rpx] pb-[26rpx]"
style="background: linear-gradient(180deg, #CCE4FE 0%, #FAFCFD 40%, #FDFDFD 100%); box-shadow: 0 4rpx 8rpx 0 rgba(0, 104, 255, 0.2);border: 1rpx solid #fff;"
>
<view class="flex gap-[30rpx]">
- <!-- 待办 -->
<view
class="h-[126rpx] w-[312rpx] flex rounded-[20rpx] pl-[38rpx] pt-[20rpx]"
style="background: url('/static/home/toDo_bg.png') no-repeat center center;
background-size: 100%;box-sizing: border-box;
box-shadow: 0 2rpx 8rpx 0 rgba(119, 144, 213, 0.3)"
+ @click="handleOpenApprovalTodo"
>
<image src="/static/home/toDo.png" class="mr-[14rpx] h-[100rpx] w-[108rpx]" />
<view class="mt-[8rpx] flex flex-col">
@@ -206,16 +416,16 @@
待办
</text>
<text class="text-[32rpx] text-[#056DFF] font-bold">
- -
+ {{ approvalTodoCount }}
</text>
</view>
</view>
- <!-- 待接待 -->
<view
class="h-[126rpx] w-[312rpx] flex items-center rounded-[20rpx] pl-[38rpx] pt-[20rpx]"
style="background: url('/static/home/wait_bg.png') no-repeat center center;
background-size: 100%;box-sizing: border-box;
box-shadow: 0 2rpx 8rpx 0 rgba(119, 144, 213, 0.3)"
+ @click="handleOpenVisitRecords"
>
<image src="/static/home/wait.png" class="mr-[14rpx] h-[100rpx] w-[108rpx]" />
<view class="mt-[8rpx] flex flex-col">
@@ -223,7 +433,7 @@
待接待
</text>
<text class="text-[32rpx] text-[#F86911] font-bold">
- -
+ {{ visitRecordCount }}
</text>
</view>
</view>
@@ -231,9 +441,7 @@
</view>
</view>
- <!-- 功能区 -->
<view class="flex gap-[16rpx] pl-[24rpx] pr-[22rpx]">
- <!-- 预约 -->
<view
class="h-[240rpx] w-[344rpx] flex flex-1 flex-col items-center justify-center rounded-[20rpx]"
style="background: linear-gradient(180deg, #DEF4FF 0%, #FFFFFF 100%);border: 1rpx solid #fff;box-shadow: 0 4rpx 10rpx 0 rgba(0, 104, 255, 0.16)"
@@ -244,10 +452,9 @@
预约
</text>
</view>
- <!-- 预约记录 & 常用来访人 -->
<view class="flex flex-1 flex-col gap-[16rpx]">
- <view
- class="flex flex-1 items-center rounded-[20rpx] px-[40rpx]"
+ <view
+ class="flex flex-1 items-center rounded-[20rpx] px-[40rpx]"
style="background: linear-gradient(180deg, #FFF7E0 0%, #FFFFFF 100%);box-shadow: 0 4rpx 10rpx 0 rgba(0, 104, 255, 0.16)"
@click="() => { router.push({ name: 'record' }) }"
>
@@ -259,6 +466,7 @@
<view
class="flex flex-1 items-center rounded-[20rpx] px-[40rpx]"
style="background: linear-gradient(180deg, #E0FAEE 0%, #FFFFFF 100%);border: 1rpx solid #fff;box-shadow: 0 4rpx 10rpx 0 rgba(0, 104, 255, 0.16)"
+ @click="uni.navigateTo({ url: '/pages/choosePerson/index?mode=manage' })"
>
<image src="/static/home/visitor.png" class="mr-[16rpx] h-[64rpx] w-[64rpx]" />
<text class="text-[28rpx] text-[#000000]">
@@ -267,74 +475,111 @@
</view>
</view>
</view>
- <!-- 登录后:我的访客码区域 -->
- <view v-if="isLoggedIn" class="mt-[32rpx] px-[24rpx]">
- <!-- 头部标题与切换 -->
+
+ <view class="mt-[32rpx] px-[24rpx]">
<view class="mb-[20rpx] flex items-center justify-between">
<text class="text-[34rpx] text-[#1a1a1a]">
我的访客码
</text>
- <view class="flex items-center">
+ <view v-if="visitorOrders.length > 0" class="flex items-center">
<image
src="/static/home/chevron_left.png"
class="h-[40rpx] w-[40rpx] pr-[24rpx]"
- @click="handlePrevVisitor"
+ @click="handlePrevOrder"
/>
<text class="text-[28rpx] text-[#999999] line-height-[40rpx]">
- {{ currentVisitorIndex + 1 }}/{{ visitorList.length }}
+ {{ currentOrderIndex + 1 }}/{{ visitorOrders.length }}
</text>
<image
src="/static/home/chevron_right.png"
class="h-[40rpx] w-[40rpx] pl-[24rpx]"
- @click="handleNextVisitor"
+ @click="handleNextOrder"
/>
</view>
</view>
- <!-- 访客码卡片轮播 -->
+ <view
+ v-if="loadingVisitorCodes"
+ class="h-[420rpx] flex items-center justify-center rounded-[12rpx] bg-white text-[28rpx] text-[#999999]"
+ style="box-shadow: 0 6rpx 10rpx 0 rgba(0, 104, 255, 0.16);"
+ >
+ 加载访客码中...
+ </view>
+
+ <view
+ v-else-if="visitorOrders.length === 0"
+ class="h-[420rpx] flex items-center justify-center rounded-[12rpx] bg-white text-[28rpx] text-[#999999]"
+ style="box-shadow: 0 6rpx 10rpx 0 rgba(0, 104, 255, 0.16);"
+ >
+ 暂无可展示的访客码
+ </view>
+
<swiper
- class="min-h-[840rpx] w-full"
- :current="currentVisitorIndex"
+ v-else
+ class="min-h-[760rpx] w-full"
+ :current="currentOrderIndex"
style="box-shadow: 0 6rpx 10rpx 0 rgba(0, 104, 255, 0.16);"
@change="onSwiperChange"
>
- <swiper-item v-for="(item) in visitorList" :key="item.id">
+ <swiper-item v-for="order in visitorOrders" :key="order.orderId">
<view
class="h-full w-full flex flex-col rounded-[12rpx] bg-white py-[24rpx]"
style="box-sizing: border-box;overflow: hidden;"
>
- <!-- 顶部信息 -->
<view class="flex flex-col items-center">
- <text class="mb-[16rpx] text-[28rpx] text-[#1a1a1a]">
- {{ item.name }}({{ item.department }})
+ <text class="mb-[16rpx] px-[32rpx] text-center text-[28rpx] text-[#1a1a1a]">
+ {{ order.visitedUserDisplay }}
</text>
<view
- class="mb-[28rpx] mt-[16rpx] h-[48rpx] w-full flex items-center justify-center"
+ class="mb-[28rpx] mt-[16rpx] h-[48rpx] w-full flex items-center justify-center px-[20rpx]"
style="background: linear-gradient(90deg, rgba(0,104,255,0) 0%, rgba(0,104,255) 50%, rgba(0,104,255,0) 100%);"
>
- <text class="text-[24rpx] text-white">
- {{ item.time }}
+ <text class="truncate text-[24rpx] text-white">
+ {{ order.timeText }}
</text>
</view>
</view>
- <!-- 二维码区 -->
- <view class="relative flex items-center justify-center px-[64rpx]">
- <image src="/static/home/left.png" class="absolute left-[32rpx] h-[48rpx] w-[24rpx]" @click="handlePrevVisitor" />
- <image :src="item.qrCode" class="h-[448rpx] w-[448rpx]" mode="aspectFit" />
- <image src="/static/home/right.png" class="absolute right-[32rpx] h-[48rpx] w-[24rpx]" @click="handleNextVisitor" />
+ <view class="relative flex items-center justify-center px-[72rpx]">
+ <image
+ v-if="order.qrCodes.length > 1"
+ src="/static/home/left.png"
+ class="absolute left-[32rpx] h-[48rpx] w-[24rpx]"
+ @click="handlePrevTicket(order.orderId)"
+ />
+ <image
+ :src="currentOrder?.orderId === order.orderId ? currentTicket?.qrCode : order.qrCodes[0]?.qrCode"
+ class="h-[360rpx] w-[360rpx]"
+ mode="aspectFit"
+ />
+ <image
+ v-if="order.qrCodes.length > 1"
+ src="/static/home/right.png"
+ class="absolute right-[32rpx] h-[48rpx] w-[24rpx]"
+ @click="handleNextTicket(order.orderId)"
+ />
</view>
- <!-- 访客名称 -->
- <view class="mt-[20rpx] flex justify-center pb-[36rpx]">
+ <view class="mt-[12rpx] flex flex-col items-center justify-center gap-[8rpx] pb-[24rpx]">
<text class="text-[24rpx] text-[rgba(26,26,26,0.5)]">
- {{ item.visitorName }}的访客码
+ {{ (currentOrder?.orderId === order.orderId ? currentTicket?.visitorName : order.qrCodes[0]?.visitorName) }}的访客码
+ </text>
+ <text
+ v-if="order.accessAreaText"
+ class="px-[40rpx] text-center text-[22rpx] text-[rgba(26,26,26,0.45)]"
+ >
+ 通行区域:{{ order.accessAreaText }}
+ </text>
+ <text v-if="order.qrCodes.length > 1" class="text-[22rpx] text-[rgba(26,26,26,0.45)]">
+ {{ currentTicketIndex(order.orderId) + 1 }}/{{ order.qrCodes.length }}
</text>
</view>
- <!-- 底部操作 -->
- <view class="h-[112rpx] flex items-center justify-center gap-[32rpx] pt-[28rpx]" style="border-top: 2rpx dashed #D8D8D8;border-radius: 12rpx;">
- <view class="w-[144rpx] flex flex-col items-center justify-center" @click="handleDownloadVisitorCode(item.qrCode)">
+ <view class="h-[104rpx] flex items-center justify-center gap-[32rpx] pt-[20rpx]" style="border-top: 2rpx dashed #D8D8D8;border-radius: 12rpx;">
+ <view
+ class="w-[144rpx] flex flex-col items-center justify-center"
+ @click="handleDownloadVisitorCode(currentOrder?.orderId === order.orderId ? currentTicket?.qrCode || '' : order.qrCodes[0]?.qrCode || '')"
+ >
<image src="/static/home/download.png" class="mb-[8rpx] h-[40rpx] w-[40rpx]" />
<text class="text-[24rpx] text-[#1a1a1a]">
下载访客码
@@ -343,6 +588,8 @@
<button
class="m-0 w-[144rpx] flex flex-col items-center justify-center border-none bg-transparent p-0 line-height-normal after:border-none"
open-type="share"
+ :data-order-uuid="order.orderUuid"
+ :data-visited-user-display="order.visitedUserDisplay"
>
<image src="/static/home/share.png" class="mb-[8rpx] h-[40rpx] w-[40rpx]" />
<text class="text-[24rpx] text-[#1a1a1a]">
@@ -355,55 +602,20 @@
</swiper>
</view>
- <!-- 备案号 (登录后显示在底部) -->
- <view v-if="isLoggedIn" class="mt-[48rpx] flex justify-center pb-[32rpx]">
+ <view class="mt-[48rpx] flex justify-center pb-[32rpx]">
<text class="text-[24rpx] text-[#999999]">
备案号:2435345342
</text>
</view>
</view>
- <!-- 悬浮流程图标 -->
- <image src="/static/home/process.png" class="fixed bottom-[482rpx] right-[-42rpx] z-5 h-[128rpx] w-[128rpx]" />
-
- <!-- 未登录提示弹窗 -->
- <wd-popup v-model="showLoginPopup" custom-class="login-popup" position="center">
- <view
- class="box-border w-[590rpx] flex flex-col items-center px-[40rpx] pb-[32rpx] pt-[32rpx]"
- style="background: linear-gradient(180deg, #D1E6FE 0%, #FFFFFF 34%, #FFFFFF 100%);"
- >
- <!-- 标题 -->
- <text class="text-[36rpx] text-[#000000] font-bold leading-[50rpx]">
- 提示
- </text>
-
- <!-- 图标 -->
- <image src="/static/home/tips.png" class="mt-[56rpx] h-[202rpx] w-[202rpx]" mode="aspectFit" />
-
- <!-- 提示文案 -->
- <text class="mt-[14rpx] text-[32rpx] text-[#000000] leading-[44rpx]">
- 您还未登录,请先登录
- </text>
-
- <!-- 操作按钮 -->
- <view class="mt-[32rpx] w-full flex justify-between gap-[24rpx]">
- <view class="h-[80rpx] flex flex-1 items-center justify-center rounded-[40rpx] bg-[#F5F5F5]" @click="handleCancelLogin">
- <text class="text-[32rpx] text-[#989898] font-medium">
- 再想想
- </text>
- </view>
- <button
- class="m-0 h-[80rpx] flex flex-1 items-center justify-center rounded-[40rpx] border-none bg-[#0C6FFF] p-0 after:border-none"
- open-type="getPhoneNumber"
- @getphonenumber="handleConfirmLogin"
- >
- <text class="text-[32rpx] text-[#FFFFFF] font-medium">
- 去登录
- </text>
- </button>
- </view>
- </view>
- </wd-popup>
+ <image
+ src="/static/home/process.png"
+ class="fixed right-[-42rpx] z-5 h-[128rpx] w-[128rpx]"
+ :style="{ bottom: `${parkFlowBottomPx}px` }"
+ @click="handleOpenParkFlow"
+ />
+ <canvas canvas-id="visitorQrCanvas" class="qr-canvas" />
</view>
</template>
@@ -416,14 +628,12 @@
font-family: 'YouSheBiaoTiHei', sans-serif;
}
-.login-text {
- display: inline-block;
- position: relative;
- border-bottom: 4rpx solid #fff;
- padding-bottom: 2rpx;
-}
-
-:deep(.login-popup) {
- border-radius: 32rpx;
+.qr-canvas {
+ position: fixed;
+ left: -9999px;
+ top: -9999px;
+ width: 300px;
+ height: 300px;
+ pointer-events: none;
}
</style>
--
Gitblit v1.7.1