整合api请求、添加购买会员卡页面、登陆页面
This commit is contained in:
@@ -36,7 +36,7 @@
|
||||
<image :src="image" mode="" :style="{width: Math.min(width, 500) + 'rpx',height: Math.min(height, 500) + 'rpx' } "></image>
|
||||
</view>
|
||||
<!-- 加载动画区域 - 设置最小尺寸确保进入页面时可见 -->
|
||||
<view v-if="!image || STQRC" class="loadingBox" :style="{width: (width > 0 ? Math.min(width, 500) : 500) + 'rpx',height: (height > 0 ? Math.min(height, 500) : 500) + 'rpx' }">
|
||||
<view v-if="(!image || STQRC) && isHaveCard" class="loadingBox" :style="{width: (width > 0 ? Math.min(width, 500) : 500) + 'rpx',height: (height > 0 ? Math.min(height, 500) : 500) + 'rpx' }">
|
||||
<view class="loading-spinner">
|
||||
<view v-if="!isCheckIn" class="spinner-circle"></view>
|
||||
<view v-else>
|
||||
@@ -47,7 +47,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 二维码装饰边框 -->
|
||||
<view v-else class="qr-border" :style="{width: Math.min(width, 500) + 80 + 'rpx',height: Math.min(height, 500) + 80 + 'rpx' }">
|
||||
<view v-else-if="image" class="qr-border" :style="{width: Math.min(width, 500) + 80 + 'rpx',height: Math.min(height, 500) + 80 + 'rpx' }">
|
||||
<view class="corner top-left"></view>
|
||||
<view class="corner top-right"></view>
|
||||
<view class="corner bottom-left"></view>
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
<!-- 状态组件(传递状态和自定义错误文案)- 已签到时不显示 -->
|
||||
<QrStatus
|
||||
v-if="!isCheckIn"
|
||||
v-if="isHaveCard && !isCheckIn"
|
||||
:status="status"
|
||||
:errorText="errorText"
|
||||
/>
|
||||
@@ -82,7 +82,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部按钮(仅保留刷新二维码,隐藏签到和清除缓存按钮) -->
|
||||
<!-- 底部按钮 -->
|
||||
<view class="bottom-actions">
|
||||
<button class="btn-refresh" @tap="refreshQR">
|
||||
<uni-icons type="refresh" size="36rpx" color="#5E6F8D"></uni-icons>
|
||||
@@ -97,10 +97,8 @@ import { ref } from 'vue';
|
||||
import { onLoad, onUnload } from '@dcloudio/uni-app'
|
||||
// 引入状态组件(路径与你保持一致)
|
||||
import QrStatus from '@/components/QRCode/StatusCard.vue'
|
||||
import { getQRCode, getMyMemberCards } from '@/api/main.js'
|
||||
|
||||
// 测试模式配置
|
||||
const TEST_MODE = true // 开启测试模式
|
||||
|
||||
let image = ref("")
|
||||
let width = ref(0)
|
||||
let height = ref(0)
|
||||
@@ -110,6 +108,7 @@ import QrStatus from '@/components/QRCode/StatusCard.vue'
|
||||
const QRStatus = ref("生成中...")
|
||||
const STQRC = ref(false)//是否扫码
|
||||
const isCheckIn = ref(false)
|
||||
const isHaveCard = ref(true)
|
||||
const webSoketURL = "ws://localhost:8084/webSocket/checkIn"
|
||||
|
||||
const qrcode = ref("")
|
||||
@@ -204,198 +203,100 @@ import QrStatus from '@/components/QRCode/StatusCard.vue'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有与签到相关的缓存(用于测试阶段)
|
||||
*/
|
||||
const clearQRCache = () => {
|
||||
try {
|
||||
const keys = uni.getStorageInfoSync().keys || []
|
||||
let clearedCount = 0
|
||||
for (const key of keys) {
|
||||
// 清除 QR_ 开头的缓存(页面内部缓存)
|
||||
if (key.startsWith(CACHE_PREFIX)) {
|
||||
uni.removeStorageSync(key)
|
||||
clearedCount++
|
||||
}
|
||||
// 清除 API_CACHE_ 开头的缓存(通过 utils/cache.js 缓存的接口数据)
|
||||
if (key.startsWith('API_CACHE_')) {
|
||||
// 只清除与签到相关的 API 缓存
|
||||
if (key.includes('checkIn') || key.includes('qrcode')) {
|
||||
uni.removeStorageSync(key)
|
||||
clearedCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`已清除 ${clearedCount} 个签到相关缓存`)
|
||||
return clearedCount
|
||||
} catch (e) {
|
||||
console.error('清除 QR 缓存失败:', e)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 测试用:手动清除缓存按钮点击事件
|
||||
*/
|
||||
const handleClearCache = () => {
|
||||
uni.showModal({
|
||||
title: '清除缓存',
|
||||
content: '确定要清除所有签到相关的缓存吗?(测试用)',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const clearedCount = clearQRCache()
|
||||
// 重置页面状态
|
||||
image.value = ""
|
||||
width.value = 0
|
||||
height.value = 0
|
||||
status.value = 'loading'
|
||||
QRStatus.value = "生成中..."
|
||||
STQRC.value = false
|
||||
isCheckIn.value = false
|
||||
|
||||
uni.showToast({
|
||||
title: `已清除 ${clearedCount} 个缓存`,
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
// 重置页面状态后不再自动请求二维码
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
onLoad(async () => {
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
// 测试模式下不显示全局loading,让页面内的加载动画显示
|
||||
if (!TEST_MODE) {
|
||||
|
||||
try {
|
||||
const memberInfo = uni.getStorageSync('memberInfo')
|
||||
console.log(memberInfo)
|
||||
const memberId = memberInfo?.id || memberInfo?.memberId
|
||||
console.log('[签到页面] 会员ID:', memberId)
|
||||
|
||||
if (!memberId) {
|
||||
throw new Error('未获取到会员ID')
|
||||
}
|
||||
|
||||
const cardRes = await getMyMemberCards(memberId)
|
||||
console.log('[签到页面] 查询会员卡结果:', JSON.stringify(cardRes, null, 2))
|
||||
|
||||
const hasCard = cardRes && cardRes.data && cardRes.data.length > 0
|
||||
console.log('[签到页面] 查询会员卡 - hasCard:', hasCard)
|
||||
|
||||
if (!hasCard) {
|
||||
isHaveCard.value = false
|
||||
uni.hideLoading()
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '您还没有会员卡,无法进行签到。是否去购买会员卡?',
|
||||
confirmText: '去购买',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.redirectTo({
|
||||
url: '/pages/memberInfo/purchaseCard'
|
||||
})
|
||||
} else {
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
isHaveCard.value = true
|
||||
uni.showLoading({
|
||||
title: '生成签到二维码...',
|
||||
mask: true
|
||||
})
|
||||
}
|
||||
|
||||
// 测试模式:直接生成假二维码,内容为"欢迎来到活氧舱"
|
||||
if (TEST_MODE) {
|
||||
console.log("测试模式:生成假二维码")
|
||||
generateTestQRCode()
|
||||
return
|
||||
}
|
||||
|
||||
// 读取签到状态缓存(自动检查过期)
|
||||
// isCheckIn 代表签到状态,从缓存读取
|
||||
const cachedIsCheckIn = getCacheData("isCheckIn")
|
||||
// checkInTime 代表具体签到时间,从缓存读取(显示在 loading-text 中)
|
||||
const cachedCheckInTime = getCacheData("checkInTime")
|
||||
|
||||
if(cachedIsCheckIn != null) {
|
||||
console.log("进入缓存 - 签到状态")
|
||||
isCheckIn.value = cachedIsCheckIn
|
||||
STQRC.value = true
|
||||
}
|
||||
|
||||
// 如果已经签到成功,直接显示成功状态,不需要请求后端
|
||||
if(isCheckIn.value) {
|
||||
console.log("已签到且有缓存,无需请求后端")
|
||||
// 读取二维码图片缓存用于显示
|
||||
const cachedQRInfo = getCacheData("QRInfo")
|
||||
if(cachedQRInfo) {
|
||||
image.value = cachedQRInfo.qrCodeBase64
|
||||
width.value = cachedQRInfo.width * 2
|
||||
height.value = cachedQRInfo.height * 2
|
||||
|
||||
const cachedIsCheckIn = getCacheData("isCheckIn")
|
||||
const cachedCheckInTime = getCacheData("checkInTime")
|
||||
|
||||
if(cachedIsCheckIn != null) {
|
||||
console.log("进入缓存 - 签到状态")
|
||||
isCheckIn.value = cachedIsCheckIn
|
||||
STQRC.value = true
|
||||
}
|
||||
// QRStatus 显示具体签到时间(从缓存读取,显示在 loading-text 中)
|
||||
QRStatus.value = cachedCheckInTime || "已完成签到"
|
||||
|
||||
if(isCheckIn.value) {
|
||||
console.log("已签到且有缓存,无需请求后端")
|
||||
const cachedQRInfo = getCacheData("QRInfo")
|
||||
if(cachedQRInfo) {
|
||||
image.value = cachedQRInfo.qrCodeBase64
|
||||
width.value = cachedQRInfo.width * 2
|
||||
height.value = cachedQRInfo.height * 2
|
||||
}
|
||||
QRStatus.value = cachedCheckInTime || "已完成签到"
|
||||
uni.hideLoading()
|
||||
return
|
||||
}
|
||||
|
||||
QRStatus.value = "生成中..."
|
||||
getStorage(null)
|
||||
} catch (err) {
|
||||
console.error('[签到页面] 查询会员卡失败:', err)
|
||||
uni.hideLoading()
|
||||
return
|
||||
uni.showToast({
|
||||
title: '获取会员卡信息失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 未签到或缓存失效,需要请求后端获取二维码
|
||||
// QRStatus 重置为默认的请求状态
|
||||
QRStatus.value = "生成中..."
|
||||
getStorage(null)
|
||||
})
|
||||
|
||||
// 测试模式:生成假二维码(内容为"欢迎来到活氧舱")
|
||||
// 不发送请求,使用缓存机制避免重复请求,二维码内容保持不变
|
||||
// @param {boolean} isRefresh - 是否是刷新操作(true=点击刷新按钮,false=首次进入页面)
|
||||
const generateTestQRCode = (isRefresh = false) => {
|
||||
// 检查是否有缓存的二维码图片
|
||||
const cachedQRImage = getCacheData("TestQRImage")
|
||||
const qrContent = "欢迎来到活氧舱"
|
||||
|
||||
if (cachedQRImage) {
|
||||
// 使用缓存的二维码图片,不发送请求
|
||||
console.log("测试模式:使用缓存的二维码图片")
|
||||
image.value = cachedQRImage.image
|
||||
width.value = cachedQRImage.width
|
||||
height.value = cachedQRImage.height
|
||||
qrcode.value = qrContent
|
||||
status.value = 'waiting'
|
||||
QRStatus.value = '请出示二维码签到'
|
||||
uni.hideLoading()
|
||||
|
||||
// 只有刷新操作才显示提示
|
||||
if (isRefresh) {
|
||||
uni.showToast({
|
||||
title: '二维码已刷新',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 首次生成,发送一次请求并缓存
|
||||
console.log("测试模式:首次生成二维码,发送请求并缓存")
|
||||
setTimeout(() => {
|
||||
qrcode.value = qrContent
|
||||
|
||||
const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodeURIComponent(qrContent)}`
|
||||
|
||||
uni.request({
|
||||
url: qrUrl,
|
||||
method: 'GET',
|
||||
responseType: 'arraybuffer',
|
||||
success: (res) => {
|
||||
const base64 = uni.arrayBufferToBase64(res.data)
|
||||
const qrImage = `data:image/png;base64,${base64}`
|
||||
|
||||
image.value = qrImage
|
||||
width.value = 500
|
||||
height.value = 500
|
||||
status.value = 'waiting'
|
||||
QRStatus.value = '请出示二维码签到'
|
||||
|
||||
// 缓存二维码图片,后续刷新不再请求
|
||||
setCacheData("TestQRImage", {
|
||||
image: qrImage,
|
||||
width: 500,
|
||||
height: 500,
|
||||
content: qrContent
|
||||
})
|
||||
|
||||
uni.hideLoading()
|
||||
},
|
||||
fail: () => {
|
||||
image.value = ''
|
||||
width.value = 500
|
||||
height.value = 500
|
||||
status.value = 'waiting'
|
||||
QRStatus.value = '测试模式 - 欢迎来到活氧舱'
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
}, 500)
|
||||
}
|
||||
|
||||
// 页面卸载时关闭WebSocket连接(不清除缓存,让缓存自然过期)
|
||||
onUnload(() => {
|
||||
closeWebSocket()
|
||||
// 缓存会在当日23:59:59自动过期,页面卸载时不主动清除
|
||||
// 如需测试,使用页面上的"清除缓存"按钮手动清除
|
||||
})
|
||||
|
||||
// 获取二维码接口
|
||||
@@ -439,16 +340,6 @@ import QrStatus from '@/components/QRCode/StatusCard.vue'
|
||||
image.value = ""
|
||||
QRStatus.value = "正在刷新二维码..."
|
||||
|
||||
// 测试模式:重新生成二维码,但不发送请求,内容不变
|
||||
if (TEST_MODE) {
|
||||
// 延迟显示,让用户看到刷新效果
|
||||
setTimeout(() => {
|
||||
generateTestQRCode(true) // 传递 isRefresh=true
|
||||
}, 300)
|
||||
return
|
||||
}
|
||||
|
||||
// 非测试模式:正常刷新逻辑
|
||||
setTimeout(() => {
|
||||
getStorage(null)
|
||||
}, 500)
|
||||
@@ -486,12 +377,6 @@ import QrStatus from '@/components/QRCode/StatusCard.vue'
|
||||
scanType: ['qrCode'],
|
||||
success: (res) => {
|
||||
console.log('扫码结果:', res)
|
||||
// 测试模式下,如果扫描的是我们生成的二维码内容,直接模拟签到成功
|
||||
if (TEST_MODE && res.result === '欢迎来到活氧舱') {
|
||||
console.log('测试模式:模拟签到成功')
|
||||
handleTestModeCheckIn()
|
||||
return
|
||||
}
|
||||
checkIn(res.result)
|
||||
},
|
||||
fail: (err) => {
|
||||
@@ -504,28 +389,6 @@ import QrStatus from '@/components/QRCode/StatusCard.vue'
|
||||
})
|
||||
}
|
||||
|
||||
// 测试模式:模拟签到成功(不请求后端)
|
||||
const handleTestModeCheckIn = () => {
|
||||
closeWebSocket()
|
||||
const now = new Date()
|
||||
const dateTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||||
|
||||
status.value = 'scanned'
|
||||
errorText.value = ''
|
||||
QRStatus.value = `${dateTime} 成功签到`
|
||||
isCheckIn.value = true
|
||||
STQRC.value = true
|
||||
|
||||
setCacheData("checkInTime", QRStatus.value)
|
||||
setCacheData("isCheckIn", isCheckIn.value)
|
||||
|
||||
uni.showToast({
|
||||
title: '签到成功!',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
|
||||
// 手动签到接口
|
||||
const checkIn = (qrContent) => {
|
||||
console.log(qrContent)
|
||||
@@ -690,6 +553,7 @@ import QrStatus from '@/components/QRCode/StatusCard.vue'
|
||||
socketTask = null
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -1021,25 +885,6 @@ import QrStatus from '@/components/QRCode/StatusCard.vue'
|
||||
}
|
||||
}
|
||||
|
||||
/* 测试用:清除缓存按钮 */
|
||||
.btn-clear-cache {
|
||||
width: calc(100% - 96rpx);
|
||||
height: 88rpx;
|
||||
background: #FEF2F2;
|
||||
color: #EF4444;
|
||||
border: 1rpx solid #FECACA;
|
||||
border-radius: 999px;
|
||||
font-size: 29rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
|
||||
&:active {
|
||||
background: #FEE2E2;
|
||||
}
|
||||
}
|
||||
|
||||
/* 旋转动画 */
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
|
||||
Reference in New Issue
Block a user