// upload.ts - 主功能页面(拍照/相册选择) import { IAppOption, IUserInfo } from 'miniprogram/types/app' import apiManager from '../../utils/api' import imageManager from '../../utils/image' import ActionSheet, { ActionSheetTheme } from 'tdesign-miniprogram/action-sheet/index'; import { FILE_BASE_URL } from '../../utils/config' const app = getApp() type IDayType = 'morning' | 'afternoon' | 'night' const DayTypeMap: Record = { morning: '早上好', afternoon: '下午好', night: '晚上好' } Page({ data: { DayTypeMap, current_date: '', currentYear: new Date().getFullYear(), // 添加当前年份 day_type: 'morning' as IDayType, isLoggedIn: false, showLoginView: false, userInfo: null as IUserInfo | null, isProcessing: false, // 是否正在处理图片 // 移除模拟数据,改为从API获取 groupedHistory: [] as any[], todaySummary: [] as any[], // 添加今日摘要数据 // 添加分页相关数据 dailyPage: 1, dailySize: 10, dailyTotal: 0, dailyHasMore: true, todayPage: 1, todaySize: 10, // 添加加载状态 isLoading: true, // 添加文件基础URL fileBaseUrl: FILE_BASE_URL, // 添加表情位置状态 facePosition: 'normal' as 'up' | 'down' | 'normal', // SVG data for avatar avatarSvgData: '', paperSvgData: '', photoSvgData: '', scrollTop: 0 }, onLoad() { console.log('主功能页面加载') this.checkLoginStatus().then(() => { // 只有登录成功后才加载历史数据 if (this.data.isLoggedIn) { this.loadTodaySummary() // 加载今日摘要数据 this.loadDailySummary() this.checkDayType() } }).catch((error) => { console.error('登录检查失败:', error) }) this.generateAvatarSvg() this.generatePhotoSvg() }, // Generate SVG data URL for avatar generateAvatarSvg() { const svgString = ` `.trim(); const encodedSvg = encodeURIComponent(svgString); const dataUrl = `data:image/svg+xml;utf8,${encodedSvg}`; this.setData({ avatarSvgData: dataUrl }); }, generatePhotoSvg() { const svgString = ` `.trim(); const encodedSvg = encodeURIComponent(svgString); const dataUrl = `data:image/svg+xml;utf8,${encodedSvg}`; this.setData({ photoSvgData: dataUrl }); }, onShow() { // 每次显示页面时检查登录状态和清理处理状态 this.updateLoginStatus() this.setData({ isProcessing: false }) // 清理处理状态 }, // 处理页面滚动 onPageScroll(e: WechatMiniprogram.Page.IPageScrollOption): void { const scrollTop = e.scrollTop const lastScrollTop = this.data.scrollTop const threshold = 30 // 滚动阈值 // 更新滚动位置 this.setData({ scrollTop }) // 向上滚动 if (scrollTop < lastScrollTop && scrollTop > threshold) { if (this.data.facePosition !== 'up') { this.setData({ facePosition: 'up' }) } } // 向下滚动 else if (scrollTop > lastScrollTop && scrollTop > threshold) { if (this.data.facePosition !== 'down') { this.setData({ facePosition: 'down' }) } } // 回到顶部附近 else if (scrollTop <= threshold) { if (this.data.facePosition !== 'normal') { this.setData({ facePosition: 'normal' }) } } }, checkDayType() { const currentHour = new Date().getHours(); const currentDate = new Date().toLocaleDateString('zh-CN'); const dayType = currentHour >= 6 && currentHour < 12 ? 'morning' : currentHour >= 12 && currentHour < 18 ? 'afternoon' : 'night'; // const dayType = 'morning'; this.setData({ current_date: currentDate, day_type: dayType as IDayType }); // 根据日间/夜间模式设置导航栏颜色 wx.setNavigationBarColor({ frontColor: '#ffffff', backgroundColor: dayType === 'night' ? '#232946' : '#fffffe', animation: { duration: 300, timingFunc: 'easeIn' } }); }, // 加载每日摘要数据(历史记录,支持分页) async loadDailySummary(page: number = 1) { try { // 只有在第一页时才显示加载状态(下拉刷新) if (page === 1) { this.setData({ isLoading: true }); } const result = await apiManager.getDailySummary(page, this.data.dailySize); // 处理数据,按年份分组 const processedItems = result.items.map(item => ({ ...item, images: item.image_ids && item.thumbnail_ids ? item.image_ids.map((imageId: string, index: number) => ({ image_id: imageId, thumbnail_id: item.thumbnail_ids![index], // 不再直接构造URL,而是存储文件ID,稍后通过安全方式获取 thumbnail_file_id: item.thumbnail_ids![index], image_file_id: imageId, thumbnail_url: '', // 将在页面渲染前通过安全方式获取 image_url: '', // 将在需要时通过安全方式获取 thumbnail_loading: true // 添加加载状态 })) : [] })); let groupedArray; if (page === 1) { // 第一页:替换所有数据 const currentYear = new Date().getFullYear(); const grouped: any = {}; processedItems.forEach(item => { const date = new Date(item.summary_time); const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); if (!grouped[year]) { grouped[year] = []; } grouped[year].push({ ...item, monthDay: `${month}-${day}` }); }); // 转换为数组形式并按年份排序 groupedArray = Object.keys(grouped) .map(year => ({ year: parseInt(year), isCurrentYear: parseInt(year) === currentYear, items: grouped[year] })) .sort((a, b) => b.year - a.year); } else { // 后续页面:只处理新数据并追加到现有数据 const currentYear = new Date().getFullYear(); const existingData = [...this.data.groupedHistory]; // 处理新数据的分组 const newGrouped: any = {}; processedItems.forEach(item => { const date = new Date(item.summary_time); const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); if (!newGrouped[year]) { newGrouped[year] = []; } newGrouped[year].push({ ...item, monthDay: `${month}-${day}` }); }); // 将新数据合并到现有数据中 Object.keys(newGrouped).forEach(year => { const yearInt = parseInt(year); const existingYearGroup = existingData.find(group => group.year === yearInt); if (existingYearGroup) { // 如果年份已存在,追加到该年份的数据中 existingYearGroup.items = [...existingYearGroup.items, ...newGrouped[year]]; } else { // 如果年份不存在,添加新的年份组 existingData.push({ year: yearInt, isCurrentYear: yearInt === currentYear, items: newGrouped[year] }); } }); // 重新排序年份 groupedArray = existingData.sort((a, b) => b.year - a.year); } this.setData({ groupedHistory: groupedArray, dailyPage: page, dailyTotal: result.total, dailyHasMore: page * this.data.dailySize < result.total, isLoading: false, }, () => { // 在数据设置完成后获取安全的图片URL this.fetchSecureImageUrls(); }); } catch (error) { console.error('加载每日摘要失败:', error); this.setData({ isLoading: false }); wx.showToast({ title: '加载失败', icon: 'none' }); } }, // 加载今日摘要数据(当天记录,只加载第一页) async loadTodaySummary(page: number = 1) { try { const result = await apiManager.getTodaySummary(page, this.data.todaySize); // 处理数据,按时间分组 const processedItems = result.items.map(item => { // 为今天的数据添加 monthDay 字段 const date = new Date(item.summary_time); const month = date.getMonth() + 1; const day = date.getDate(); const monthDay = `${month}-${day}`; return { ...item, monthDay: monthDay, // 添加 monthDay 字段 images: item.image_ids && item.thumbnail_ids ? item.image_ids.map((imageId: string, index: number) => ({ image_id: imageId, thumbnail_id: item.thumbnail_ids![index], // 不再直接构造URL,而是存储文件ID,稍后通过安全方式获取 thumbnail_file_id: item.thumbnail_ids![index], image_file_id: imageId, thumbnail_url: '', // 将在页面渲染前通过安全方式获取 image_url: '', // 将在需要时通过安全方式获取 thumbnail_loading: true // 添加加载状态 })) : [] }; }); // 设置今日摘要数据 this.setData({ todaySummary: processedItems }, () => { // 在数据设置完成后获取安全的图片URL this.fetchSecureTodayImageUrls(); }); console.log('今日摘要数据加载完成:', processedItems); return processedItems; } catch (error) { console.error('加载今日摘要失败:', error); wx.showToast({ title: '加载今日数据失败', icon: 'none' }); throw error; } }, // 触底加载更多数据(只加载每日摘要的下一页) onReachBottom() { if (this.data.dailyHasMore && !this.data.isLoading) { const nextPage = this.data.dailyPage + 1; this.loadDailySummary(nextPage); } }, // 下拉刷新 onPullDownRefresh() { this.setData({ dailyPage: 1, todayPage: 1 }); this.loadDailySummary(1).then(() => { wx.stopPullDownRefresh(); }).catch(() => { wx.stopPullDownRefresh(); }); }, // 获取安全的图片URL async fetchSecureImageUrls() { try { console.log('开始获取安全的图片URL'); // 遍历所有分组的历史记录 const updatedHistory = [...this.data.groupedHistory]; let hasChanges = false; for (const yearGroup of updatedHistory) { for (const item of yearGroup.items) { for (const image of item.images) { // 如果还没有获取过安全URL且有thumbnail_file_id if ((!image.thumbnail_url || image.thumbnail_url === '') && image.thumbnail_file_id) { try { // 首先检查本地缓存中是否已有该缩略图 const cachedThumbnail = this.getCachedThumbnail(image.thumbnail_file_id); if (cachedThumbnail) { image.thumbnail_url = cachedThumbnail; image.thumbnail_loading = false; hasChanges = true; // console.log('使用缓存的缩略图:', image.thumbnail_file_id); continue; } // 获取安全的缩略图URL image.thumbnail_url = await apiManager.getFileDisplayUrl(image.thumbnail_file_id); // 缓存缩略图到本地存储 this.cacheThumbnail(image.thumbnail_file_id, image.thumbnail_url); image.thumbnail_loading = false; // 设置加载完成状态 hasChanges = true; } catch (error) { console.error('获取缩略图URL失败:', error); // 如果获取失败,使用默认的占位图 image.thumbnail_url = '/static/sun-2.png'; image.thumbnail_loading = false; // 设置加载完成状态 } } // 如果还没有获取过安全URL且有image_file_id // if (!image.image_url && image.image_file_id) { // try { // // 获取安全的原图URL // image.image_url = await apiManager.getFileDisplayUrl(image.image_file_id); // hasChanges = true; // } catch (error) { // console.error('获取原图URL失败:', error); // // 如果获取失败,使用默认的占位图 // image.image_url = '/static/placeholder.png'; // } // } } } } // 如果有变化,更新数据 if (hasChanges) { this.setData({ groupedHistory: updatedHistory }); // console.log('安全图片URL获取完成'); } } catch (error) { console.error('获取安全图片URL失败:', error); } }, // 获取安全的今日摘要图片URL async fetchSecureTodayImageUrls() { try { console.log('开始获取今日摘要安全的图片URL'); // 获取今日摘要数据 const todaySummary = [...this.data.todaySummary]; let hasChanges = false; for (const item of todaySummary) { for (const image of item.images) { // 如果还没有获取过安全URL且有thumbnail_file_id if ((!image.thumbnail_url || image.thumbnail_url === '') && image.thumbnail_file_id) { try { // 首先检查本地缓存中是否已有该缩略图 const cachedThumbnail = this.getCachedThumbnail(image.thumbnail_file_id); if (cachedThumbnail) { image.thumbnail_url = cachedThumbnail; image.thumbnail_loading = false; hasChanges = true; // console.log('使用缓存的缩略图:', image.thumbnail_file_id); continue; } // 获取安全的缩略图URL image.thumbnail_url = await apiManager.getFileDisplayUrl(image.thumbnail_file_id); // 缓存缩略图到本地存储 this.cacheThumbnail(image.thumbnail_file_id, image.thumbnail_url); image.thumbnail_loading = false; // 设置加载完成状态 hasChanges = true; } catch (error) { console.error('获取缩略图URL失败:', error); // 如果获取失败,使用默认的占位图 image.thumbnail_url = '/static/sun-2.png'; image.thumbnail_loading = false; // 设置加载完成状态 } } } } // 如果有变化,更新数据 if (hasChanges) { this.setData({ todaySummary: todaySummary }); // console.log('安全图片URL获取完成'); } } catch (error) { console.error('获取安全图片URL失败:', error); } }, // 缓存缩略图到本地存储 cacheThumbnail(fileId: string, filePath: string) { try { // 获取文件信息以计算大小 const fs = wx.getFileSystemManager(); fs.getFileInfo({ filePath: filePath, success: (res) => { const fileSize = res.size; // 创建缓存条目 const cacheEntry = { filePath: filePath, timestamp: Date.now(), size: fileSize }; // 保存到本地存储 wx.setStorageSync(`thumbnail_cache_${fileId}`, cacheEntry); // console.log('缩略图已缓存:', fileId, '大小:', fileSize, 'bytes'); }, fail: (err) => { console.error('获取缩略图文件信息失败:', err); } }); } catch (error) { console.error('缓存缩略图失败:', error); } }, // 从本地存储获取缓存的缩略图 getCachedThumbnail(fileId: string): string | null { try { const cacheKey = `thumbnail_cache_${fileId}`; const cacheEntry = wx.getStorageSync(cacheKey); if (cacheEntry) { // 检查文件是否存在 const fs = wx.getFileSystemManager(); try { fs.accessSync(cacheEntry.filePath); // 检查缓存是否过期(例如7天) const cacheAge = Date.now() - cacheEntry.timestamp; const maxCacheAge = 7 * 24 * 60 * 60 * 1000; // 7天 if (cacheAge < maxCacheAge) { return cacheEntry.filePath; } else { // 缓存过期,删除缓存条目 wx.removeStorageSync(cacheKey); console.log('缩略图缓存已过期并已删除:', fileId); } } catch (err) { // 文件不存在,删除缓存条目 wx.removeStorageSync(cacheKey); console.log('缩略图文件不存在,已删除缓存条目:', fileId); } } return null; } catch (error) { console.error('获取缓存缩略图失败:', error); return null; } }, // 图片点击事件 onImageTap(e: any) { const { imageId } = e.currentTarget.dataset; if (imageId) { wx.navigateTo({ url: `/pages/assessment/assessment?imageId=${imageId}` }) } }, onImageCardTap(e: any) { const { imageItems } = e.currentTarget.dataset; if (!imageItems?.images?.length) return; const items = imageItems.images.map((item: any) => ({ image_id: item.image_id, image: item.thumbnail_loading ? '' : (item.thumbnail_url || '/static/sun-2.png'), // 使用安全URL或占位图 })); ActionSheet.show({ theme: ActionSheetTheme.Grid, selector: '#t-images-sheet', context: this, align: 'center', description: '请选择图片', items, cancelText: '取消' }); }, // 检查登录状态 async checkLoginStatus() { try { // 使用智能登录检查和更新登录状态 const loginResult = await apiManager.smartLogin() if (loginResult) { // 登录成功,更新全局状态 app.globalData.isLoggedIn = true app.globalData.token = loginResult.access_token this.updateLoginStatus() console.log('登录状态验证成功') return true } else { // 登录失败,跳转到登录页 console.log('登录状态验证失败,跳转到登录页') wx.reLaunch({ url: '/pages/index/index' }) return false } } catch (error) { console.error('检查登录状态失败:', error) // 发生错误时跳转到登录页 wx.reLaunch({ url: '/pages/index/index' }) return false } }, // 更新登录状态 updateLoginStatus() { const isLoggedIn = !!app.globalData.token this.setData({ isLoggedIn, showLoginView: !isLoggedIn }) }, // 处理图片选择(合并拍照和相册选择功能) handleImageSelect() { if (this.data.isProcessing) return; ActionSheet.show({ theme: ActionSheetTheme.List, selector: '#t-action-sheet', context: this, align: 'center', description: '请选择操作', items: [ { label: '拍照识别', icon: 'camera' }, { label: '相册选择', icon: 'image' } ], cancelText: '取消' }); }, handleSelected(e: any) { const { index } = e.detail; console.log('用户选择:', index) if (index === 0) { this.handleTakePhoto(); } else if (index === 1) { this.handleChooseFromAlbum(); } }, handleImageSelected(e: any) { console.log('用户选择:', e) if (e.detail.selected === 'cancel') return; const { image_id } = e.detail.selected if (image_id) { wx.navigateTo({ url: `/pages/assessment/assessment?imageId=${image_id}` }) } }, // 拍照 async handleTakePhoto() { try { this.setData({ isProcessing: true }) wx.showLoading({ title: '准备拍照...' }) const imagePath = await imageManager.takePhoto({ quality: 80, maxWidth: 1920, maxHeight: 1920 }) wx.hideLoading() console.log('拍照成功:', imagePath) // 直接跳转到结果页面 this.navigateToResult(imagePath) } catch (error: any) { wx.hideLoading() this.setData({ isProcessing: false }) console.error('拍照失败:', error) if (error?.message !== '用户取消选择') { wx.showToast({ title: '拍照失败', icon: 'none' }) } } }, // 从相册选择 async handleChooseFromAlbum() { try { this.setData({ isProcessing: true }) wx.showLoading({ title: '准备选择图片...' }) // const imagePath = await imageManager.chooseFromAlbum({ // quality: 80, // maxWidth: 1920, // maxHeight: 1920 // }) const imagePath = await imageManager.chooseFromAlbum() wx.hideLoading() console.log('选择图片成功:', imagePath) // 直接跳转到结果页面 this.navigateToResult(imagePath) } catch (error: any) { wx.hideLoading() this.setData({ isProcessing: false }) console.error('选择图片失败:', error) if (error?.message !== '用户取消选择') { wx.showToast({ title: '选择图片失败', icon: 'none' }) } } }, // 跳转到结果页面 navigateToResult(imagePath: string) { try { console.log('跳转到结果页面:', imagePath) // 跳转到结果页面并传递图片路径 wx.navigateTo({ url: `/pages/result/result?imagePath=${encodeURIComponent(imagePath)}` }) } catch (error) { console.error('跳转结果页面失败:', error) this.setData({ isProcessing: false }) wx.showToast({ title: '操作失败', icon: 'none' }) } }, // 跳转到个人主页 goProfile() { wx.navigateTo({ url: '/pages/profile/profile' }) } })