Files
WXProgram/miniprogram/utils/avatarUtils.ts
2025-10-26 13:15:04 +08:00

353 lines
8.9 KiB
TypeScript
Raw Permalink 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.

// 头像工具类 - 处理头像相关的功能
/**
* 头像尺寸配置
*/
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<string> 缩放后的图片路径
*/
static async resizeAvatar(src: string, width: number, height: number): Promise<string> {
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<string> 裁剪后的圆形头像路径
*/
static async cropAvatarToCircle(src: string, size: number = AvatarSizes.MEDIUM): Promise<string> {
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<string> 压缩后的图片路径
*/
static async compressAvatar(src: string, quality: number = AvatarQuality.STANDARD): Promise<string> {
return new Promise((resolve, reject) => {
wx.compressImage({
src,
quality,
success: (res) => {
resolve(res.tempFilePath);
},
fail: reject
});
});
}
/**
* 缓存头像图片
* @param avatarUrl 头像URL
* @param cacheKey 缓存键
* @returns Promise<string> 缓存后的图片路径
*/
static async cacheAvatar(avatarUrl: string, cacheKey?: string): Promise<string> {
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<string> 下载后的临时文件路径
*/
private static downloadAvatar(url: string): Promise<string> {
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<boolean>
*/
private static checkFileExists(filePath: string): Promise<boolean> {
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;