Files
danyang-match/miniapp/pages/profile-photos/profile-photos.js
T

195 lines
5.8 KiB
JavaScript
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.
/**
* 头像与照片页 U08(Phase 04 P04-T23)
*
* 重构要点(遵循 AGENTS.md 不变量 + Phase 04 验收条件):
* - 数据:profileService.getPhotos(每次从服务端获取,不在本地缓存图片 URL —— 验收"图片URL非永久公开")
* - 上传:profileService.requestUploadToken + wx.uploadFile(EXIF 由服务端清理,前端不处理)
* - 删除:profileService.deletePhoto
* - 主头像:profileService.setMainPhoto(第一张或用户指定)
* - 数量限制:BUSINESS_LIMITS.MAX_PROFILE_PHOTOS(第 12 条,不硬编码)
* - 敏感数据:本页不涉及身份证/手机号(第 8 条)
* - 下一步条件:至少 1 张主头像
*/
const { appStore } = require('../../stores');
const { profileService } = require('../../services');
const { ROUTE, BUSINESS_LIMITS } = require('../../constants');
Page({
data: {
statusBarHeight: 20,
navBarHeight: 44,
loading: false,
saving: false,
avatarPhotos: [],
lifestylePhotos: [],
maxLifestyle: BUSINESS_LIMITS.MAX_PROFILE_PHOTOS - 1,
// 页面说明
guidelines: [
'请使用本人近期真实照片',
'禁止网图、二维码、广告、联系方式图片',
'建议真人生活照,避免过度美颜',
],
},
onLoad() {
this._initSystemInfo();
this.loadPhotos();
},
_initSystemInfo() {
try {
const sysInfo = wx.getSystemInfoSync();
this.setData({
statusBarHeight: sysInfo.statusBarHeight || 20,
navBarHeight: 44,
});
} catch (e) { /* 使用默认值 */ }
},
async loadPhotos() {
this.setData({ loading: true });
try {
const result = await profileService.getPhotos();
const photos = (result && result.photos) || [];
const avatarPhotos = photos.filter((p) => p.photoType === 'avatar');
const lifestylePhotos = photos.filter((p) => p.photoType === 'lifestyle');
this.setData({ avatarPhotos, lifestylePhotos, loading: false });
} catch (err) {
this.setData({ loading: false });
appStore.toastError(err, '加载照片失败');
}
},
// ============ 上传 ============
/** 主头像区添加 */
onAddAvatar() {
this._chooseAndUpload('avatar');
},
/** 生活照区添加 */
onAddLifestyle() {
const remaining = this.data.maxLifestyle - this.data.lifestylePhotos.length;
if (remaining <= 0) {
appStore.toast(`最多上传 ${BUSINESS_LIMITS.MAX_PROFILE_PHOTOS} 张照片`);
return;
}
this._chooseAndUpload('lifestyle');
},
_chooseAndUpload(photoType) {
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
success: (res) => {
const tempFile = res.tempFiles[0];
if (!tempFile) return;
this._uploadPhoto(tempFile.tempFilePath, photoType);
},
fail: () => { /* 用户取消 */ },
});
},
async _uploadPhoto(filePath, photoType) {
appStore.showLoading('上传中…');
try {
// 1. 获取上传凭证(EXIF 由服务端清理)
const fileName = `photo_${Date.now()}.jpg`;
const tokenRes = await profileService.requestUploadToken({ fileName, contentType: 'image/jpeg' });
// 2. 上传到对象存储
await new Promise((resolve, reject) => {
wx.uploadFile({
url: tokenRes.uploadUrl,
filePath,
name: 'file',
formData: { 'Content-Type': 'image/jpeg' },
success: (res) => {
if (res.statusCode >= 200 && res.statusCode < 300) resolve(res);
else reject(new Error('上传失败'));
},
fail: reject,
});
});
// 3. 若上传的是头像区,或当前无主头像,则设为主头像
if (photoType === 'avatar' || this.data.avatarPhotos.length === 0) {
await profileService.setMainPhoto(tokenRes.photoId);
}
appStore.hideLoading();
appStore.toastSuccess('上传成功');
this.loadPhotos();
} catch (err) {
appStore.hideLoading();
appStore.toastError(err, '上传失败');
}
},
// ============ 删除 ============
async onRemoveAvatar(e) {
const { index } = e.detail;
const photo = this.data.avatarPhotos[index];
if (!photo) return;
this._removePhoto(photo.photoId);
},
async onRemoveLifestyle(e) {
const { index } = e.detail;
const photo = this.data.lifestylePhotos[index];
if (!photo) return;
this._removePhoto(photo.photoId);
},
async _removePhoto(photoId) {
const confirm = await appStore.confirm('确定删除这张照片?', '删除后不可恢复');
if (!confirm) return;
appStore.showLoading('删除中…');
try {
await profileService.deletePhoto(photoId);
appStore.hideLoading();
appStore.toastSuccess('已删除');
this.loadPhotos();
} catch (err) {
appStore.hideLoading();
appStore.toastError(err, '删除失败');
}
},
// ============ 预览 ============
onPreviewAvatar(e) {
const { index } = e.detail;
const photo = this.data.avatarPhotos[index];
if (photo) this._preview(photo.url);
},
onPreviewLifestyle(e) {
const { index } = e.detail;
const photo = this.data.lifestylePhotos[index];
if (photo) this._preview(photo.url);
},
_preview(currentUrl) {
const urls = [
...this.data.avatarPhotos.map((p) => p.url),
...this.data.lifestylePhotos.map((p) => p.url),
].filter(Boolean);
if (urls.length === 0) return;
wx.previewImage({ urls, current: currentUrl });
},
// ============ 导航 ============
onBack() {
wx.navigateBack();
},
onNext() {
if (this.data.avatarPhotos.length === 0) {
appStore.toast('请至少上传 1 张主头像');
return;
}
wx.navigateTo({ url: ROUTE.ONBOARDING.PROFILE_INTERESTS });
},
});