整合api请求、添加购买会员卡页面、登陆页面

This commit is contained in:
future
2026-06-23 22:17:53 +08:00
parent 1c547a717e
commit 8d8c823616
70 changed files with 7666 additions and 2656 deletions
+88 -243
View File
@@ -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); }