Files
miniprogram-1/miniprogram/pages/profile/profile.ts
2025-12-12 20:23:28 +08:00

748 lines
21 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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,
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: '1.0.0', // 默认版本
buildDate: '2024-01-01'
}
},
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.loadAllDataAsync()
const endTime = Date.now()
logger.info('个人中心页面显示完成,耗时:', endTime - startTime, 'ms')
},
// 初始化页面
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, 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,
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'
}
},
/**
* 更换头像方法
* 点击头像区域触发此方法
*/
changeAvatar() {
// TODO: 实现更换头像的逻辑
logger.info('更换头像方法被触发');
// 这里可以添加选择图片、上传图片等逻辑
},
// 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'
});
}
}
})