// 头像工具类 - 处理头像相关的功能 /** * 头像尺寸配置 */ export const AvatarSizes = { // 小尺寸 - 用于列表、地图标记点等 SMALL: 40, // 中等尺寸 - 用于用户面板、详情页等 MEDIUM: 80, // 大尺寸 - 用于个人资料页等 LARGE: 120, // 超大尺寸 - 用于编辑页面等 XLARGE: 160 } as const; /** * 头像质量配置 */ export const AvatarQuality = { // 缩略图质量 THUMBNAIL: 60, // 标准质量 STANDARD: 80, // 高质量 HIGH: 90 } as const; /** * 默认头像配置 */ export const DefaultAvatars = { // 默认用户头像 USER: '/images/user-avatar.png', // 管理员头像 ADMIN: '/images/admin-avatar.png', // 员工头像 EMPLOYEE: '/images/employee-avatar.png', // 货运人员头像 DELIVERY_PERSON: '/images/delivery-avatar.png' } as const; /** * 头像工具类 */ export class AvatarUtils { /** * 获取默认头像URL * @param role 用户角色 * @returns 默认头像URL */ static getDefaultAvatar(role?: string): string { switch (role) { case 'ADMIN': return DefaultAvatars.ADMIN; case 'DELIVERY_PERSON': return DefaultAvatars.DELIVERY_PERSON; case 'EMPLOYEE': return DefaultAvatars.EMPLOYEE; default: return DefaultAvatars.USER; } } /** * 生成头像占位符文本 * @param name 用户名 * @returns 占位符文本 */ static generatePlaceholderText(name?: string): string { if (!name) return '👤'; // 如果是中文名字,取最后一个字符 if (/[\u4e00-\u9fa5]/.test(name)) { return name.charAt(name.length - 1); } // 如果是英文名字,取首字母 return name.charAt(0).toUpperCase(); } /** * 获取头像显示URL * @param avatarUrl 原始头像URL * @param size 目标尺寸 * @param quality 图片质量 * @returns 处理后的头像URL */ static getAvatarUrl(avatarUrl: string, _size: number = AvatarSizes.MEDIUM, _quality: number = AvatarQuality.STANDARD): string { if (!avatarUrl) { return ''; } // 如果是本地临时文件,直接返回 if (avatarUrl.startsWith('http://tmp/') || avatarUrl.startsWith('wxfile://')) { return avatarUrl; } // 如果是网络图片,可以添加尺寸参数(如果后端支持) if (avatarUrl.startsWith('http')) { // 这里可以根据后端API添加尺寸参数 // 例如:return `${avatarUrl}?width=${size}&quality=${quality}`; return avatarUrl; } return avatarUrl; } /** * 缩放头像图片 * @param src 原始图片路径 * @param width 目标宽度 * @param height 目标高度 * @returns Promise 缩放后的图片路径 */ static async resizeAvatar(src: string, width: number, height: number): Promise { return new Promise((resolve, reject) => { if (!src) { reject(new Error('图片路径不能为空')); return; } wx.getImageInfo({ src, success: (_infoRes) => { const canvasId = `resize-canvas-${Date.now()}`; const ctx = wx.createCanvasContext(canvasId); // 绘制缩放后的图片 ctx.drawImage(src, 0, 0, width, height); ctx.draw(false, () => { wx.canvasToTempFilePath({ canvasId, destWidth: width, destHeight: height, success: (res) => { resolve(res.tempFilePath); }, fail: reject }); }); }, fail: reject }); }); } /** * 裁剪头像为圆形 * @param src 原始图片路径 * @param size 裁剪尺寸 * @returns Promise 裁剪后的圆形头像路径 */ static async cropAvatarToCircle(src: string, size: number = AvatarSizes.MEDIUM): Promise { return new Promise((resolve, reject) => { if (!src) { reject(new Error('图片路径不能为空')); return; } wx.getImageInfo({ src, success: (_infoRes) => { const canvasId = `circle-canvas-${Date.now()}`; const ctx = wx.createCanvasContext(canvasId); // 绘制圆形裁剪区域 ctx.save(); ctx.beginPath(); ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI); ctx.clip(); // 绘制图片 ctx.drawImage(src, 0, 0, size, size); ctx.restore(); ctx.draw(false, () => { wx.canvasToTempFilePath({ canvasId, destWidth: size, destHeight: size, success: (res) => { resolve(res.tempFilePath); }, fail: reject }); }); }, fail: reject }); }); } /** * 压缩头像图片 * @param src 原始图片路径 * @param quality 压缩质量 (0-100) * @returns Promise 压缩后的图片路径 */ static async compressAvatar(src: string, quality: number = AvatarQuality.STANDARD): Promise { return new Promise((resolve, reject) => { wx.compressImage({ src, quality, success: (res) => { resolve(res.tempFilePath); }, fail: reject }); }); } /** * 缓存头像图片 * @param avatarUrl 头像URL * @param cacheKey 缓存键 * @returns Promise 缓存后的图片路径 */ static async cacheAvatar(avatarUrl: string, cacheKey?: string): Promise { if (!avatarUrl) { return ''; } // 如果是本地文件,直接返回 if (avatarUrl.startsWith('http://tmp/') || avatarUrl.startsWith('wxfile://')) { return avatarUrl; } const key = cacheKey || `avatar_${avatarUrl.replace(/[^a-zA-Z0-9]/g, '_')}`; try { // 检查缓存 const cachedPath = wx.getStorageSync(key); if (cachedPath) { // 验证缓存文件是否存在 try { await this.checkFileExists(cachedPath); return cachedPath; } catch (error) { // 缓存文件不存在,重新下载 console.log('缓存文件不存在,重新下载:', avatarUrl); } } // 下载头像 const downloadRes = await this.downloadAvatar(avatarUrl); // 缓存头像路径 wx.setStorageSync(key, downloadRes); return downloadRes; } catch (error) { console.error('缓存头像失败:', error); return avatarUrl; } } /** * 下载头像图片 * @param url 头像URL * @returns Promise 下载后的临时文件路径 */ private static downloadAvatar(url: string): Promise { return new Promise((resolve, reject) => { wx.downloadFile({ url, success: (res) => { if (res.statusCode === 200) { resolve(res.tempFilePath); } else { reject(new Error(`下载失败,状态码: ${res.statusCode}`)); } }, fail: reject }); }); } /** * 检查文件是否存在 * @param filePath 文件路径 * @returns Promise */ private static checkFileExists(filePath: string): Promise { return new Promise((resolve) => { wx.getFileInfo({ filePath, success: () => resolve(true), fail: () => resolve(false) }); }); } /** * 生成头像样式类名 * @param size 尺寸 * @param shape 形状 * @param bordered 是否有边框 * @param shadow 是否有阴影 * @returns 样式类名字符串 */ static generateAvatarClass(size: number, shape: 'circle' | 'square' = 'circle', bordered: boolean = false, shadow: boolean = false): string { const classes = []; // 尺寸类 if (size <= AvatarSizes.SMALL) { classes.push('avatar-small'); } else if (size <= AvatarSizes.MEDIUM) { classes.push('avatar-medium'); } else if (size <= AvatarSizes.LARGE) { classes.push('avatar-large'); } else { classes.push('avatar-xlarge'); } // 形状类 classes.push(shape === 'circle' ? 'avatar-circle' : 'avatar-square'); // 边框类 if (bordered) { classes.push('avatar-bordered'); } // 阴影类 if (shadow) { classes.push('avatar-shadow'); } return classes.join(' '); } /** * 清理头像缓存 * @param cacheKey 缓存键(可选,不传则清理所有头像缓存) */ static clearAvatarCache(cacheKey?: string) { if (cacheKey) { wx.removeStorageSync(cacheKey); } else { // 清理所有以 avatar_ 开头的缓存 const storageInfo = wx.getStorageInfoSync(); storageInfo.keys.forEach(key => { if (key.startsWith('avatar_')) { wx.removeStorageSync(key); } }); } } } export default AvatarUtils;