838 lines
24 KiB
TypeScript
Executable File
838 lines
24 KiB
TypeScript
Executable File
// profile.ts - 个人中心页面
|
||
import apiManager from '../../utils/api'
|
||
import { IAppOption, IUserInfo } from '../../types/app'
|
||
import logger from '../../utils/logger'
|
||
|
||
const app = getApp<IAppOption>()
|
||
|
||
Page({
|
||
data: {
|
||
isLoggedIn: false,
|
||
userInfo: null as IUserInfo | null,
|
||
|
||
// SVG data for avatar
|
||
avatarSvgData: '',
|
||
|
||
showClearConfirm: false,
|
||
dialogKey: '',
|
||
|
||
// 积分数据
|
||
points: {
|
||
balance: 0,
|
||
available_balance: 0,
|
||
frozen_balance: 0,
|
||
total_purchased: 0,
|
||
total_refunded: 0,
|
||
expired_time: ''
|
||
},
|
||
|
||
// 兑换码弹窗显示控制
|
||
showCouponInput: false,
|
||
couponCode: '',
|
||
couponCodeTips: '',
|
||
// 键盘与弹窗联动
|
||
keyboardHeight: 0,
|
||
windowHeight: 0,
|
||
couponDialogStyle: '',
|
||
couponPlaceholderClass: 'placeholder',
|
||
couponDialogInitialPending: false,
|
||
couponInputHidden: false,
|
||
|
||
// 缓存统计信息
|
||
cacheStats: {
|
||
totalSize: '0KB',
|
||
details: '暂无缓存数据'
|
||
},
|
||
|
||
// 统计信息
|
||
stats: {
|
||
totalRecognitions: 0,
|
||
todayRecognitions: 0,
|
||
imageRecognitions: 0,
|
||
favoriteWords: 0
|
||
},
|
||
|
||
// 设置选项
|
||
settings: {
|
||
autoSave: true,
|
||
highQuality: false,
|
||
soundEnabled: true,
|
||
vibrationEnabled: true
|
||
},
|
||
|
||
// 词典等级配置
|
||
dictLevel: 'level1',
|
||
dictLevelOptions: apiManager.getDictLevelOptions(),
|
||
|
||
// 应用信息
|
||
appInfo: {
|
||
version: '0.25.1222', // 默认版本
|
||
buildDate: '2025-12-22'
|
||
}
|
||
},
|
||
|
||
onLoad() {
|
||
logger.info('个人中心页面加载')
|
||
this.initPage()
|
||
this.generateAvatarSvg()
|
||
// 记录窗口高度
|
||
try {
|
||
const sys = wx.getSystemInfoSync()
|
||
this.setData({ windowHeight: sys.windowHeight || 0 })
|
||
} catch (e) {}
|
||
// 键盘高度监听
|
||
try {
|
||
wx.onKeyboardHeightChange((res) => {
|
||
const height = res.height || 0
|
||
this.setData({ keyboardHeight: height })
|
||
this.updateCouponDialogStyle()
|
||
})
|
||
} catch (e) {}
|
||
},
|
||
|
||
onShow() {
|
||
logger.info('个人中心页面显示')
|
||
const startTime = Date.now()
|
||
|
||
// 更新用户信息(快速操作)
|
||
this.updateUserInfo()
|
||
|
||
// 获取最新头像
|
||
this.fetchUserAvatar()
|
||
|
||
// 异步加载用户数据,不阻塞页面显示
|
||
this.loadAllDataAsync()
|
||
|
||
const endTime = Date.now()
|
||
logger.info('个人中心页面显示完成,耗时:', endTime - startTime, 'ms')
|
||
},
|
||
|
||
// 获取用户头像
|
||
async fetchUserAvatar() {
|
||
if (!this.data.isLoggedIn) return;
|
||
try {
|
||
const { url } = await apiManager.getUserAvatar();
|
||
if (url && url !== this.data.userInfo?.avatar_url) {
|
||
const userInfo = this.data.userInfo || {} as IUserInfo;
|
||
const updatedUser = { ...userInfo, avatar_url: url };
|
||
|
||
this.setData({ userInfo: updatedUser });
|
||
app.globalData.userInfo = updatedUser;
|
||
wx.setStorageSync('userInfo', updatedUser);
|
||
logger.info('User avatar updated from server', { url });
|
||
}
|
||
} catch (error) {
|
||
logger.error('Failed to fetch user avatar', error);
|
||
}
|
||
},
|
||
|
||
// 初始化页面
|
||
async initPage() {
|
||
try {
|
||
logger.info('开始初始化个人中心页面')
|
||
// wx.setNavigationBarColor({
|
||
// frontColor: '#000000',
|
||
// backgroundColor: '#F5F5F5'
|
||
// });
|
||
const startTime = Date.now()
|
||
|
||
// 先设置基础数据,避免页面空白
|
||
this.setBasicData()
|
||
|
||
// 检查登录状态
|
||
if (!this.checkLoginStatus()) {
|
||
return
|
||
}
|
||
|
||
const endTime = Date.now()
|
||
logger.info('个人中心页面初始化完成,耗时:', endTime - startTime, 'ms')
|
||
} catch (error) {
|
||
logger.error('个人中心页面初始化失败:', error)
|
||
this.setBasicData() // 即使失败也要显示基础界面
|
||
}
|
||
},
|
||
|
||
// 设置基础数据,避免页面空白
|
||
setBasicData() {
|
||
try {
|
||
logger.info('设置基础数据')
|
||
const basicData = {
|
||
isLoggedIn: app.globalData.isLoggedIn || false,
|
||
userInfo: app.globalData.userInfo || null,
|
||
dictLevel: app.globalData.dictLevel || wx.getStorageSync('dictLevel') || 'level1'
|
||
}
|
||
|
||
// 设置基础统计数据
|
||
const stats = {
|
||
totalRecognitions: 0,
|
||
todayRecognitions: 0,
|
||
imageRecognitions: 0,
|
||
favoriteWords: 0
|
||
}
|
||
|
||
// 设置基础缓存统计
|
||
const cacheStats = {
|
||
totalSize: '0KB',
|
||
details: '加载中...'
|
||
}
|
||
|
||
this.setData({
|
||
...basicData,
|
||
cacheStats: cacheStats,
|
||
stats: stats
|
||
})
|
||
|
||
logger.info('基础数据设置完成')
|
||
} catch (error) {
|
||
logger.error('设置基础数据失败:', error)
|
||
}
|
||
},
|
||
|
||
// 异步加载所有数据,不阻塞页面显示
|
||
async loadAllDataAsync() {
|
||
try {
|
||
logger.info('开始异步加载所有数据')
|
||
const startTime = Date.now()
|
||
|
||
// 并行加载所有数据,使用异步方式避免阻塞页面显示
|
||
await Promise.all([
|
||
// this.loadUserStats(),
|
||
// this.loadDictLevel(),
|
||
this.loadCacheStats(),
|
||
this.loadPointsData()
|
||
])
|
||
|
||
// 更新用户信息
|
||
this.updateUserInfo()
|
||
|
||
const endTime = Date.now()
|
||
logger.info('所有数据异步加载完成,耗时:', endTime - startTime, 'ms')
|
||
} catch (error) {
|
||
logger.error('异步加载数据失败:', error)
|
||
}
|
||
},
|
||
|
||
// 检查登录状态
|
||
checkLoginStatus() {
|
||
logger.info('检查登录状态:', {
|
||
isLoggedIn: app.globalData.isLoggedIn,
|
||
hasToken: !!app.globalData.token,
|
||
userInfo: app.globalData.userInfo
|
||
})
|
||
|
||
if (!app.globalData.isLoggedIn) {
|
||
logger.info('用户未登录,准备跳转到登录页')
|
||
wx.showToast({
|
||
title: '请先登录',
|
||
icon: 'none'
|
||
})
|
||
|
||
setTimeout(() => {
|
||
wx.reLaunch({
|
||
url: '/pages/index/index'
|
||
})
|
||
}, 1500)
|
||
return false
|
||
}
|
||
|
||
logger.info('用户已登录,继续加载页面')
|
||
return true
|
||
},
|
||
|
||
// 更新用户信息
|
||
updateUserInfo() {
|
||
const userData = {
|
||
isLoggedIn: app.globalData.isLoggedIn || false,
|
||
userInfo: app.globalData.userInfo || null,
|
||
dictLevel: app.globalData.dictLevel || wx.getStorageSync('dictLevel') || 'level1'
|
||
}
|
||
|
||
logger.info('更新用户信息:', userData)
|
||
this.setData(userData)
|
||
},
|
||
|
||
// 加载用户统计信息
|
||
async loadUserStats() {
|
||
try {
|
||
logger.info('开始加载用户统计信息')
|
||
const startTime = Date.now()
|
||
|
||
// 调用API获取审核统计数据
|
||
const auditStats = await apiManager.getAuditStatistics()
|
||
|
||
const stats = {
|
||
totalRecognitions: auditStats.total_count,
|
||
todayRecognitions: auditStats.today_count,
|
||
imageRecognitions: auditStats.image_count,
|
||
favoriteWords: this.data.stats.favoriteWords // 保持原有值
|
||
}
|
||
|
||
const endTime = Date.now()
|
||
logger.info('统计信息加载完成,耗时:', endTime - startTime, 'ms', stats)
|
||
this.setData({ stats })
|
||
} catch (error) {
|
||
logger.error('加载统计信息失败:', error)
|
||
// 设置默认统计数据
|
||
this.setData({
|
||
stats: {
|
||
totalRecognitions: 0,
|
||
todayRecognitions: 0,
|
||
imageRecognitions: 0,
|
||
favoriteWords: 0
|
||
}
|
||
})
|
||
}
|
||
},
|
||
|
||
// 加载词典等级配置
|
||
loadDictLevel() {
|
||
return new Promise<void>((resolve) => {
|
||
try {
|
||
logger.info('开始加载词典等级配置')
|
||
const startTime = Date.now()
|
||
|
||
const dictLevel = app.globalData.dictLevel || wx.getStorageSync('dictLevel') || 'level1'
|
||
|
||
const endTime = Date.now()
|
||
logger.info('词典等级配置加载完成,耗时:', endTime - startTime, 'ms', dictLevel)
|
||
this.setData({ dictLevel })
|
||
resolve()
|
||
} catch (error) {
|
||
logger.error('加载词典等级配置失败:', error)
|
||
// 使用默认设置
|
||
this.setData({ dictLevel: 'level1' })
|
||
resolve()
|
||
}
|
||
})
|
||
},
|
||
|
||
// 加载积分数据
|
||
async loadPointsData() {
|
||
try {
|
||
logger.info('开始加载积分数据')
|
||
const startTime = Date.now()
|
||
|
||
// 调用API获取积分数据
|
||
const pointsData = await apiManager.getPointsData()
|
||
|
||
// 如果返回的数据为null,则设置默认值
|
||
const finalPointsData = pointsData || { balance: 0, available_balance: 0, frozen_balance: 0, total_purchased: 0, total_refunded: 0, expired_time: '' }
|
||
|
||
const endTime = Date.now()
|
||
logger.info('积分数据加载完成,耗时:', endTime - startTime, 'ms', finalPointsData)
|
||
this.setData({ points: finalPointsData })
|
||
} catch (error) {
|
||
logger.error('加载积分数据失败:', error)
|
||
// 设置默认积分数据
|
||
this.setData({
|
||
points: {
|
||
balance: 0,
|
||
available_balance: 0,
|
||
frozen_balance: 0,
|
||
total_purchased: 0,
|
||
total_refunded: 0,
|
||
expired_time: ''
|
||
}
|
||
})
|
||
}
|
||
},
|
||
|
||
// 加载缓存统计信息
|
||
loadCacheStats() {
|
||
return new Promise<void>((resolve) => {
|
||
try {
|
||
logger.info('开始加载缓存统计信息')
|
||
const startTime = Date.now()
|
||
|
||
const stats = apiManager.getCacheStats()
|
||
const formattedTotalSize = apiManager.formatFileSize(stats.totalSize)
|
||
|
||
let details = '暂无缓存数据'
|
||
if (stats.totalSize > 0) {
|
||
const detailParts = []
|
||
const imageTotal = (stats.sizeByType.image || 0) + (stats.sizeByType.thumbnail || 0)
|
||
// 图片和缩略图的总大小
|
||
if (imageTotal > 0) {
|
||
detailParts.push(`文件:${apiManager.formatFileSize(imageTotal)}`)
|
||
}
|
||
// 音频和词典的总大小
|
||
const fileTotal = (stats.sizeByType.audio || 0) + (stats.sizeByType.dict || 0)
|
||
if (fileTotal > 0) {
|
||
detailParts.push(`数据:${apiManager.formatFileSize(fileTotal)}`)
|
||
}
|
||
details = detailParts.join(',')
|
||
}
|
||
|
||
const endTime = Date.now()
|
||
logger.info('缓存统计信息加载完成,耗时:', endTime - startTime, 'ms', { totalSize: formattedTotalSize, details })
|
||
|
||
this.setData({
|
||
cacheStats: {
|
||
totalSize: formattedTotalSize,
|
||
details: details
|
||
}
|
||
})
|
||
resolve()
|
||
} catch (error) {
|
||
logger.error('加载缓存统计信息失败:', error)
|
||
// 使用默认设置
|
||
this.setData({
|
||
cacheStats: {
|
||
totalSize: '0KB',
|
||
details: '加载失败'
|
||
}
|
||
})
|
||
resolve()
|
||
}
|
||
})
|
||
},
|
||
|
||
// handleAction() {
|
||
// // 将 dictLevelOptions 转换为 ActionSheet items 格式
|
||
// const items = Object.entries(this.data.dictLevelOptions).map(([key, value]) => ({
|
||
// label: value as string
|
||
// }));
|
||
|
||
// ActionSheet.show({
|
||
// theme: ActionSheetTheme.List,
|
||
// selector: '#t-action-sheet',
|
||
// context: this,
|
||
// align: 'center',
|
||
// description: '请选择词典等级',
|
||
// items: items,
|
||
// });
|
||
// },
|
||
|
||
// handleSelected(e: any) {
|
||
// console.log('词典等级选择:', e.detail);
|
||
// const { index } = e.detail;
|
||
|
||
// // 获取选中的词典等级 key
|
||
// const dictLevelKeys = Object.keys(this.data.dictLevelOptions);
|
||
// const selectedKey = dictLevelKeys[index];
|
||
|
||
// if (selectedKey) {
|
||
// console.log('选中的词典等级 key:', selectedKey);
|
||
// // 调用 updateDictLevel 方法更新配置
|
||
// this.updateDictLevel(selectedKey);
|
||
// }
|
||
// },
|
||
|
||
// 更新词典等级配置
|
||
// async updateDictLevel(newLevel: string) {
|
||
// try {
|
||
// wx.showLoading({ title: '更新中...' })
|
||
|
||
// // 调用 API 更新设置
|
||
// await apiManager.updateUserSettings({ dict_level: newLevel })
|
||
|
||
// // 更新本地状态
|
||
// this.setData({ dictLevel: newLevel })
|
||
// app.globalData.dictLevel = newLevel
|
||
|
||
// wx.showToast({
|
||
// title: '更新成功',
|
||
// icon: 'success'
|
||
// })
|
||
|
||
// console.log('词典等级更新成功:', newLevel)
|
||
// } catch (error: any) {
|
||
// console.error('更新词典等级失败:', error)
|
||
// wx.showToast({
|
||
// title: error.message || '更新失败',
|
||
// icon: 'none'
|
||
// })
|
||
// } finally {
|
||
// wx.hideLoading()
|
||
// }
|
||
// },
|
||
|
||
showDialog(e: any) {
|
||
const { key } = e.currentTarget.dataset;
|
||
this.setData({ [key]: true, dialogKey: key });
|
||
},
|
||
|
||
closeDialog() {
|
||
const { dialogKey } = this.data;
|
||
this.setData({ [dialogKey]: false });
|
||
},
|
||
|
||
// 清除缓存
|
||
handleClearCache() {
|
||
wx.showModal({
|
||
title: '清除缓存',
|
||
content: '确定要清除应用缓存吗?这将删除所有本地数据(除登录信息外)。',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
// 清除除登录信息外的所有缓存
|
||
const keysToKeep = ['token', 'userInfo', 'tokenExpiry', 'sessionUuid', 'dictLevel']
|
||
const storageInfo = wx.getStorageInfoSync()
|
||
|
||
storageInfo.keys.forEach(key => {
|
||
if (!keysToKeep.includes(key)) {
|
||
wx.removeStorageSync(key)
|
||
}
|
||
})
|
||
|
||
// 清除API管理器中的缓存
|
||
apiManager.clearAudioCache()
|
||
apiManager.clearWordDetailCache()
|
||
apiManager.clearImageCache()
|
||
apiManager.clearThumbnailCache()
|
||
|
||
wx.showToast({
|
||
title: '缓存已清除',
|
||
icon: 'success'
|
||
})
|
||
|
||
// 重新加载设置和缓存统计
|
||
this.loadDictLevel()
|
||
this.loadCacheStats()
|
||
} catch (error) {
|
||
logger.error('清除缓存失败:', error)
|
||
wx.showToast({
|
||
title: '清除失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
// 查看使用条款
|
||
handleViewTerms() {
|
||
wx.navigateTo({
|
||
url: '/pages/terms/terms'
|
||
})
|
||
},
|
||
|
||
// 查看隐私政策
|
||
handleViewPrivacy() {
|
||
wx.navigateTo({
|
||
url: '/pages/privacy/privacy'
|
||
})
|
||
},
|
||
|
||
// 意见反馈
|
||
handleFeedback() {
|
||
wx.showModal({
|
||
title: '意见反馈',
|
||
content: '如有问题或建议,请通过小程序客服功能联系我们,我们会及时处理您的反馈。',
|
||
confirmText: '联系客服',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 这里可以跳转到客服页面或打开客服会话
|
||
wx.showToast({
|
||
title: '客服功能开发中',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
// 关于我们
|
||
handleAboutUs() {
|
||
wx.showModal({
|
||
title: '关于我们',
|
||
content: `图片识别助手 v${this.data.appInfo.version}\n\n一个智能的图片识别小程序,支持文字识别、物体识别等功能。`,
|
||
showCancel: false,
|
||
confirmText: '确定'
|
||
})
|
||
},
|
||
|
||
// 跳转到历史记录页面
|
||
navigateToHistory() {
|
||
return false
|
||
// wx.navigateTo({
|
||
// url: '/pages/history/history'
|
||
// })
|
||
},
|
||
|
||
// 退出登录
|
||
handleLogout() {
|
||
wx.showModal({
|
||
title: '确认退出',
|
||
content: '确定要退出登录吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 清理登录状态
|
||
app.globalData.isLoggedIn = false
|
||
app.globalData.token = undefined
|
||
app.globalData.userInfo = undefined
|
||
app.globalData.dictLevel = undefined
|
||
|
||
wx.removeStorageSync('token')
|
||
wx.removeStorageSync('userInfo')
|
||
wx.removeStorageSync('tokenExpiry')
|
||
wx.removeStorageSync('sessionUuid')
|
||
wx.removeStorageSync('dictLevel')
|
||
|
||
wx.showToast({
|
||
title: '已退出登录',
|
||
icon: 'success'
|
||
})
|
||
|
||
setTimeout(() => {
|
||
wx.reLaunch({
|
||
url: '/pages/index/index'
|
||
})
|
||
}, 1500)
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
|
||
// 分享小程序
|
||
onShareAppMessage() {
|
||
return {
|
||
title: '图片识别助手 - 智能识别图片内容',
|
||
path: '/pages/index/index',
|
||
imageUrl: '/icon/share.png' // 需要添加分享图片
|
||
}
|
||
},
|
||
|
||
// 分享到朋友圈
|
||
onShareTimeline() {
|
||
return {
|
||
title: '图片识别助手 - 智能识别图片内容',
|
||
query: '',
|
||
imageUrl: '/icon/share.png'
|
||
}
|
||
},
|
||
|
||
// 头像裁剪完成回调
|
||
async onAvatarCropped(filePath: string) {
|
||
try {
|
||
wx.showLoading({ title: '上传中...' });
|
||
|
||
// 1. 上传图片
|
||
const fileId = await apiManager.uploadImageFile(filePath);
|
||
|
||
// 2. 更新用户信息
|
||
const { url } = await apiManager.updateUserAvatar(fileId);
|
||
|
||
// 3. 更新本地数据
|
||
const userInfo = this.data.userInfo || {} as IUserInfo;
|
||
const updatedUser = { ...userInfo, avatar_url: url };
|
||
|
||
this.setData({
|
||
userInfo: updatedUser
|
||
});
|
||
|
||
// 更新全局数据
|
||
app.globalData.userInfo = updatedUser;
|
||
wx.setStorageSync('userInfo', updatedUser);
|
||
|
||
wx.hideLoading();
|
||
wx.showToast({ title: '更换成功', icon: 'success' });
|
||
logger.info('Avatar updated successfully', { fileId });
|
||
|
||
} catch (error: any) {
|
||
wx.hideLoading();
|
||
logger.error('Failed to change avatar', error);
|
||
wx.showToast({
|
||
title: error.message || '更换失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 更换头像方法
|
||
* 点击头像区域触发此方法
|
||
*/
|
||
changeAvatar() {
|
||
logger.info('更换头像方法被触发');
|
||
|
||
// 检查登录状态
|
||
if (!this.data.isLoggedIn) {
|
||
wx.showToast({ title: '请先登录', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
wx.chooseMedia({
|
||
count: 1,
|
||
mediaType: ['image'],
|
||
sourceType: ['album', 'camera'],
|
||
success: (res) => {
|
||
const tempFilePath = res.tempFiles[0].tempFilePath;
|
||
// 跳转到裁剪页面
|
||
wx.navigateTo({
|
||
url: `/pages/avatar_crop/avatar_crop?src=${encodeURIComponent(tempFilePath)}`
|
||
});
|
||
},
|
||
fail: (err) => {
|
||
if (err.errMsg.indexOf('cancel') === -1) {
|
||
logger.error('Choose media failed', err);
|
||
wx.showToast({ title: '选择图片失败', icon: 'none' });
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// Generate SVG data URL for avatar
|
||
generateAvatarSvg() {
|
||
const svgString = `
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="470 50 20 40">
|
||
<path d="M 486 70 C 486 71.7614 483.761 74 481 74 C 478.239 74 476 71.7614 476 70 C 476 69 477 69 477 70 C 477 71 479 73 481 73 C 483 73 485 71 485 70 C 485 69 486 69 486 70 M 490 64 C 488.7 64 488.7 66 490 66 C 491.3 66 491.3 64 490 64 M 472 64 C 470.7 64 470.7 66 472 66 C 473.3 66 473.3 64 472 64" stroke="#001858" stroke-width="1.5" stroke-linecap="round"></path>
|
||
</svg>
|
||
`.trim();
|
||
|
||
const encodedSvg = encodeURIComponent(svgString);
|
||
const dataUrl = `data:image/svg+xml;utf8,${encodedSvg}`;
|
||
|
||
this.setData({
|
||
avatarSvgData: dataUrl
|
||
});
|
||
},
|
||
|
||
// 显示兑换码输入弹窗
|
||
showCouponDialog() {
|
||
this.setData({
|
||
showCouponInput: true,
|
||
couponCode: '',
|
||
couponDialogInitialPending: true
|
||
});
|
||
this.updateCouponDialogStyle()
|
||
// 在下一帧恢复定位
|
||
try {
|
||
wx.nextTick(() => {
|
||
this.setData({ couponDialogInitialPending: false })
|
||
this.updateCouponDialogStyle()
|
||
})
|
||
} catch (e) {}
|
||
},
|
||
|
||
// 关闭兑换码输入弹窗
|
||
closeCouponDialog() {
|
||
this.setData({
|
||
showCouponInput: false,
|
||
couponCode: '',
|
||
couponCodeTips: '' // Clear any error tips when closing dialog
|
||
});
|
||
this.setData({ couponDialogStyle: '' })
|
||
try { if ((this as any)._couponRevealTimer) clearTimeout((this as any)._couponRevealTimer) } catch (e) {}
|
||
this.setData({ couponPlaceholderClass: 'placeholder', couponInputHidden: false })
|
||
},
|
||
|
||
// '' 处理兑换码输入
|
||
onCouponInput(e: any) {
|
||
let value = e.detail.value || '';
|
||
// 只允许字母和数字
|
||
value = value.replace(/[^A-Za-z0-9]/g, '');
|
||
// 转换为大写
|
||
value = value.toUpperCase();
|
||
|
||
this.setData({
|
||
couponCode: value,
|
||
couponCodeTips: '' // Clear any previous error tips when user types
|
||
});
|
||
},
|
||
|
||
// 输入框聚焦/失焦
|
||
onCouponFocus() {
|
||
this.updateCouponDialogStyle()
|
||
this.setData({ couponPlaceholderClass: 'placeholder placeholder-transparent', couponInputHidden: true })
|
||
try { if ((this as any)._couponRevealTimer) clearTimeout((this as any)._couponRevealTimer) } catch (e) {}
|
||
;(this as any)._couponRevealTimer = setTimeout(() => {
|
||
this.setData({ couponPlaceholderClass: 'placeholder', couponInputHidden: false })
|
||
}, 240)
|
||
},
|
||
onCouponBlur() {
|
||
// 失焦后等待系统回调键盘高度变更
|
||
setTimeout(() => {
|
||
this.updateCouponDialogStyle()
|
||
}, 0)
|
||
try { if ((this as any)._couponRevealTimer) clearTimeout((this as any)._couponRevealTimer) } catch (e) {}
|
||
this.setData({ couponPlaceholderClass: 'placeholder', couponInputHidden: false })
|
||
},
|
||
|
||
// 根据键盘高度更新弹窗位置,使其在剩余可视区域垂直居中
|
||
updateCouponDialogStyle() {
|
||
const { showCouponInput, keyboardHeight, windowHeight } = this.data
|
||
if (!showCouponInput) {
|
||
this.setData({ couponDialogStyle: '', couponPlaceholderClass: 'placeholder' })
|
||
return
|
||
}
|
||
const available = Math.max(windowHeight - keyboardHeight, 0)
|
||
const offset = Math.floor(available / 2 - windowHeight / 2)
|
||
const style = `top: 50%; left: 50%; right: auto; transform: translate(-50%, -50%) translate3d(0, ${offset}px, 0); will-change: transform;`
|
||
this.setData({ couponDialogStyle: style, couponPlaceholderClass: 'placeholder' })
|
||
},
|
||
|
||
// 确认兑换码
|
||
async confirmCouponDialog() {
|
||
const couponCode = this.data.couponCode;
|
||
|
||
// 检查兑换码是否为空
|
||
if (!couponCode) {
|
||
wx.showToast({
|
||
title: '请输入兑换码',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 检查兑换码长度(假设为6位)
|
||
logger.info(couponCode)
|
||
logger.info(couponCode.length)
|
||
if (couponCode.length !== 6) {
|
||
wx.showToast({
|
||
title: '兑换码应为6位',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 显示加载提示
|
||
wx.showLoading({
|
||
title: '兑换中...'
|
||
});
|
||
|
||
// 调用API进行兑换
|
||
const response = await apiManager.redeemCoupon(couponCode);
|
||
|
||
// 隐藏加载提示
|
||
wx.hideLoading();
|
||
|
||
// 显示成功提示
|
||
wx.showToast({
|
||
title: '兑换成功',
|
||
icon: 'success'
|
||
});
|
||
|
||
// 关闭弹窗
|
||
this.closeCouponDialog();
|
||
|
||
// 如果有积分数据,更新积分显示
|
||
if (response.points) {
|
||
this.setData({
|
||
'points.balance': this.data.points.balance + response.points
|
||
});
|
||
}
|
||
} catch (error: any) {
|
||
// 隐藏加载提示
|
||
wx.hideLoading();
|
||
|
||
console.error('兑换失败:', error);
|
||
|
||
// 设置错误提示信息
|
||
const errorMessage = error.message || error.errMsg || '兑换失败,请稍后重试';
|
||
this.setData({
|
||
couponCodeTips: errorMessage
|
||
});
|
||
|
||
// 显示错误提示
|
||
wx.showToast({
|
||
title: errorMessage,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
}
|
||
})
|