Files
past-go/weapp/pages/settings/settings.js
T
Blizzard b854d2873e feat: require couple binding and refine miniapp settings
Gate record features and storage entry points with invitation guidance; enforce record access in the backend. Add homepage greetings, right-swipe profile dismissal, and move the smoking switch to settings. Simplify API base configuration and remove the unused legacy tab bar component.
2026-11-04 21:27:03 +08:00

446 lines
16 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.
const { getLayout } = require('../../utils/layout');
// pages/settings/settings.js
const { api, getImageUrl, API_BASE, uploadFileCompat } = require('../../utils/request');
const { requestSubscribe, getSubscribeStatus, isTemplateConfigured, subscribeErrorMessage } = require('../../utils/subscribe');
const { guardStorageSpace } = require('../../utils/storage-guard');
Page({
onResize: function() { this.setData(getLayout()); },
data: {
smokingVisible:true, smokingKnown:false, smokingSaving:false, smokingError:false,
showBgModal: false,
bgPreview: '',
bgFilePath: '',
hasPartner: false,
bindingKnown: false, bindingError:false,
// 解绑状态
unbindStatus: '', // '' / 'pending' / 'agreed' / 'rejected' / 'cancelled' / 'expired'
unbindIsRequester: false, // 当前用户是否是发起方
unbindRequesterName: '', // 发起方昵称
unbindRemaining: 0, // 剩余秒数
// 消息通知设置
showSubscribeModal: false,
subscribeStatus: {},
notifyAvailable: typeof isTemplateConfigured === 'function' && isTemplateConfigured('notify'),
subscribeItems: [],
subscribeLoading: false,
// 我们past
showAboutModal: false,
// 联系我们
showContactModal: false,
},
onLoad: function() {
this.setData(getLayout());},
onPullDownRefresh: function() {
wx.stopPullDownRefresh();
},
onShow: async function() {
this.loadSmokingPreference();
this.setData({ bindingKnown: false, bindingError:false });
try {
var info = await api.coupleInfo();
var hasPartner = !!(info && !info.not_bound && info.id);
this.setData({ hasPartner: hasPartner, bindingKnown: true });
if (hasPartner) {
wx.setStorageSync('coupleId', info.id);
this.loadUnbindStatus();
} else {
wx.removeStorageSync('coupleId');
this.setData({ unbindStatus: '', unbindIsRequester: false, unbindRequesterName: '' });
}
} catch (e) {
this.setData({bindingError:true});
console.error('获取绑定状态失败', e);
}
},
loadSmokingPreference: async function() {
try { const auth=await api.checkAuth(); this.setData({smokingVisible:Number(auth.user.smoking_card_visible)!==0,smokingKnown:true,smokingError:false}); }
catch(error) { this.setData({smokingError:true}); }
},
toggleSmoking: async function(e) {
if (this.data.smokingSaving || !this.data.smokingKnown) return;
const previous=this.data.smokingVisible;
this.setData({smokingSaving:true});
try { const result=await api.smokingCardVisible({visible:e.detail.value?1:0}); this.setData({smokingVisible:Number(result.card_visible)===1}); }
catch(error) { this.setData({smokingVisible:previous}); wx.showToast({title:error.error||'保存失败,请重试',icon:'none'}); }
finally { this.setData({smokingSaving:false}); }
},
goBind: function() {
getApp().globalData.openProfileBind = true;
wx.switchTab({ url: '/pages/profile/profile' });
},
deleteAccount: async function() {
if(this._deletingAccount)return;
if (this.data.hasPartner) {
wx.showModal({title:'暂无法注销',content:'请先完成情侣解绑后再注销账号;处于解绑申请或72小时冷静期内,也需要等待解绑完成。',showCancel:false,confirmText:'知道了'});
return;
}
this._deletingAccount=true;
try {
const preview=await api.accountDeletionPreview();
const counts=preview.summary.counts;
const text='已上传文件 '+counts.files+' 个,相册 '+counts.albums+' 个,照片 '+counts.photos+' 张,日记 '+counts.diaries+' 篇,记忆日 '+counts.timeline+' 条,打卡 '+counts.checkins+' 条,抽烟记录 '+counts.smoking+' 条,生理期日记录 '+counts.period+' 条。';
const labels={past_song:'歌曲',past_diary_comment:'评论',past_period_record:'生理期周期',past_period_sex_record:'亲密记录',past_storage_order:'订单',past_subscribe_template:'消息订阅'};
const more=Object.keys(labels).map(key=>labels[key]+' '+((preview.summary.record_counts||{})[key]||0)+' 条').join(',');
const confirm=options=>new Promise(resolve=>wx.showModal({...options,success:r=>resolve(r.confirm),fail:()=>resolve(false)}));
if(!await confirm({title:'注销账号前,请确认',content:text+' 其他数据:'+more+'。注销后,账号、所有内容及文件关联会被删除,无法恢复;剩余空间权益将失效。',confirmText:'继续注销',confirmColor:'#B74768'}))return;
if(!await confirm({title:'最后一次确认',content:'确定永久注销并删除全部数据?独占文件将从存储中清理;与其他用户重复的物理文件只移除你的关联,不会删除他人的内容。',confirmText:'确认注销',confirmColor:'#B74768'}))return;
wx.showLoading({title:'正在注销',mask:true});
await api.accountDelete({confirmed:true,confirmation_text:'注销并删除全部数据',confirmation_token:preview.confirmation_token});
getApp().finishAccountDeletion();
wx.reLaunch({url:'/pages/index/index'});
} catch(error) {wx.showToast({title:error.error||'注销失败,请稍后重试',icon:'none',duration:3000});}
finally {wx.hideLoading();this._deletingAccount=false;}
},
// 加载解绑状态
loadUnbindStatus: function() {
var self = this;
api.unbindStatus().then(function(res) {
if (res && res.status && res.status !== 'none') {
self.setData({
unbindStatus: res.status,
unbindIsRequester: !!res.is_requester,
unbindRequesterName: res.requester_name || '对方',
unbindRemaining: res.remaining_seconds || 0
});
} else {
self.setData({ unbindStatus: '', unbindIsRequester: false, unbindRequesterName: '' });
}
}).catch(function() {});
},
noop: function() {},
// ===== 消息通知管理(按场景独立授权) =====
showSubscribeModal: async function() {
this.setData({ showSubscribeModal: true, subscribeLoading: true });
try {
// forceRefresh=true 确保从后端获取最新状态
const status = await getSubscribeStatus(true);
this.setData({
subscribeStatus: status,
subscribeChecked: !!status['notify'],
subscribeLoading: false
});
} catch (e) {
this.setData({ subscribeLoading: false });
}
},
hideSubscribeModal: function() {
this.setData({ showSubscribeModal: false });
},
// ===== 我们past =====
showAboutModal: function() {
this.setData({ showAboutModal: true });
},
hideAboutModal: function() {
this.setData({ showAboutModal: false });
},
openPrivacyContract: function() {
if (typeof wx.openPrivacyContract !== 'function') {
wx.showToast({ title: '请升级微信后查看隐私协议', icon: 'none' });
return;
}
wx.openPrivacyContract({
fail: function() {
wx.showToast({ title: '隐私协议暂时无法打开,请稍后重试', icon: 'none' });
}
});
},
// ===== 联系我们 =====
showContactModal: function() {
this.setData({ showContactModal: true });
},
hideContactModal: function() {
this.setData({ showContactModal: false });
},
toggleSubscribe: async function(e) {
const type = 'notify';
if (typeof isTemplateConfigured === 'function' && !isTemplateConfigured(type)) {
wx.showToast({title:'该提醒暂未配置模板',icon:'none'});
return;
}
const field = 'subscribeChecked';
const current = this.data[field];
// 防止重复点击
if (this._subscribing) return;
this._subscribing = true;
if (current) {
// 取消订阅
try {
await api.unsubscribeTemplate({ template_type: type });
this.setData({ [field]: false });
wx.setStorageSync('subscribed_' + type, false);
wx.showToast({ title: '已关闭通知', icon: 'success' });
} catch (e) {
wx.showToast({ title: '操作失败', icon: 'none' });
}
} else {
// 请求订阅授权
try {
const results = await requestSubscribe(type);
if (results[type]) {
this.setData({ [field]: true });
wx.setStorageSync('subscribed_' + type, true);
wx.showToast({ title: '已开启通知', icon: 'success' });
} else {
wx.showToast({ title: '订阅被拒绝,可在设置中重新开启', icon: 'none' });
}
} catch (e) {
wx.showModal({title:'无法订阅提醒',content:subscribeErrorMessage(e),showCancel:false});
}
}
this._subscribing = false;
},
renewSubscribe: async function(e) {
const type = 'notify';
const field = 'subscribeChecked';
if (this._subscribing) return;
this._subscribing = true;
try {
const results = await requestSubscribe(type);
if (results[type]) {
this.setData({ [field]: true });
wx.showToast({ title: '已订阅一次提醒', icon: 'success' });
} else {
wx.showToast({ title: '本次未授权提醒', icon: 'none' });
}
} catch (e) {
wx.showModal({title:'无法订阅提醒',content:subscribeErrorMessage(e),showCancel:false});
} finally {
this._subscribing = false;
}
},
// ===== 背景图 =====
showBgModal: async function() {
this.setData({ showBgModal: true, bgPreview: '', bgFilePath: '' });
try {
var cfg = await api.getBgConfig();
if (!this.data.bgFilePath) this.setData({ bgPreview: cfg && cfg.image ? getImageUrl(cfg.image) : '' });
} catch (e) { console.error(e); }
},
hideBgModal: function() { this.setData({ showBgModal: false }); },
// 背景编辑页左滑返回设置页面
bgTouchStartX: 0, bgTouchStartY: 0,
onBgTouchStart: function(e) {
this.bgTouchStartX = e.touches[0].clientX;
this.bgTouchStartY = e.touches[0].clientY;
},
onBgTouchEnd: function(e) {
var dx = this.bgTouchStartX - e.changedTouches[0].clientX;
var dy = Math.abs(this.bgTouchStartY - e.changedTouches[0].clientY);
if (dx < 20 && dy < 20) return;
if (Math.abs(dx) > dy && dx > 80) this.hideBgModal();
},
chooseBgImage: function() {
var self = this;
guardStorageSpace(0).then(function(ok) {
if (!ok) return;
wx.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album'],
success: function(res) {
self.setData({ bgPreview: res.tempFilePaths[0], bgFilePath: res.tempFilePaths[0] });
}
});
});
},
saveBgImage: async function() {
var fp = this.data.bgFilePath;
if (!fp) {
if (this.data.bgPreview) {
wx.showToast({ title: '背景已设置', icon: 'success' });
this.hideBgModal();
return;
}
wx.showToast({ title: '请先选择图片', icon: 'none' }); return;
}
var self = this;
wx.showLoading({ title: '上传中...' });
try {
var data = await uploadFileCompat({
url: API_BASE + '/setBgImage',
filePath: fp,
name: 'bg',
formData: {}
});
wx.hideLoading();
if (data && data.path) {
var imgUrl = getImageUrl(data.path);
wx.showToast({ title: '背景已保存 ✨', icon: 'success' });
self.setData({ bgPreview: imgUrl, bgFilePath: '' });
self.hideBgModal();
} else {
var errMsg = (data && data.error) || '上传失败';
if (data && data.code) errMsg += ' (错误码: ' + data.code + ')';
wx.showToast({ title: errMsg, icon: 'none' });
}
} catch(e) {
wx.hideLoading();
var errMsg = (e && e.error) || '上传失败';
if (e && e.code) errMsg += ' (错误码: ' + e.code + ')';
wx.showToast({ title: errMsg, icon: 'none' });
}
},
removeBgImage: async function() {
try { await api.deleteBgImage(); } catch(e) {}
wx.showToast({ title: '已恢复默认', icon: 'success' });
this.setData({ bgPreview: '', bgFilePath: '' });
this.hideBgModal();
},
// ===== 解除绑定(根据状态显示不同操作)=====
showUnbind: function() {
var status = this.data.unbindStatus;
var isRequester = this.data.unbindIsRequester;
if (status === 'pending') {
if (isRequester) {
// 发起方:撤销解绑
this.doCancelUnbind();
} else {
// 对方:同意或拒绝
this.doRespondUnbind();
}
} else if (status === 'cooling') {
wx.showToast({ title: '正在72小时冷静期', icon: 'none' });
wx.navigateTo({ url: '/pages/unbind/unbind' });
} else {
// 无解绑请求或已结束:发起新解绑
this.doRequestUnbind();
}
},
// 发起解绑请求
doRequestUnbind: function() {
var self = this;
wx.showModal({
title: '💔 解除绑定',
content: '对方同意后进入72小时冷静期,期间任一方可取消。到期自动解绑并永久删除共同数据;个人抽烟记录保留。',
cancelText: '取消',
confirmText: '发起解绑',
confirmColor: '#FF4757',
success: function(res) {
if (res.confirm) {
wx.showLoading({ title: '发送请求...' });
api.unbindRequest().then(function() {
wx.hideLoading();
wx.showToast({ title: '解绑请求已发送', icon: 'success' });
self.loadUnbindStatus();
wx.navigateTo({ url: '/pages/unbind/unbind' });
}).catch(function(err) {
wx.hideLoading();
wx.showToast({ title: err.error || '操作失败', icon: 'none' });
});
}
}
});
},
// 撤销解绑请求
doCancelUnbind: function() {
var self = this;
wx.showModal({
title: '↩️ 撤销解绑',
content: '取消后关系继续保留。确定取消解绑吗?',
cancelText: '取消',
confirmText: '撤销请求',
confirmColor: '#FF6B9D',
success: function(res) {
if (res.confirm) {
wx.showLoading({ title: '撤销中...' });
api.unbindCancel().then(function() {
wx.hideLoading();
wx.showToast({ title: '已撤销解绑请求', icon: 'success' });
self.loadUnbindStatus();
}).catch(function(err) {
wx.hideLoading();
wx.showToast({ title: err.error || '操作失败', icon: 'none' });
});
}
}
});
},
// 回应解绑请求(对方视角)
doRespondUnbind: function() {
var self = this;
var name = this.data.unbindRequesterName;
wx.showActionSheet({
itemList: ['✅ 同意解绑', '❌ 拒绝解绑'],
success: function(res) {
if (res.tapIndex === 0) {
// 同意
wx.showModal({
title: '⚠️ 确认同意',
content: name + ' 发起了解绑请求。同意后进入72小时冷静期,期间可取消;到期自动解绑并永久删除共同数据,个人抽烟记录保留。',
cancelText: '取消',
confirmText: '同意解绑',
confirmColor: '#FF4757',
success: function(r) {
if (r.confirm) {
wx.showLoading({ title: '处理中...' });
api.unbindRespond({ agree: true }).then(function() {
wx.hideLoading();
wx.showToast({ title: '已进入72小时冷静期', icon: 'none' });
self.loadUnbindStatus();
wx.navigateTo({ url: '/pages/unbind/unbind' });
}).catch(function(err) {
wx.hideLoading();
wx.showToast({ title: err.error || '操作失败', icon: 'none' });
});
}
}
});
} else if (res.tapIndex === 1) {
// 拒绝
wx.showModal({
title: '拒绝解绑',
content: '确定要拒绝 ' + name + ' 的解绑请求吗?',
cancelText: '取消',
confirmText: '拒绝',
confirmColor: '#FF6B9D',
success: function(r) {
if (r.confirm) {
wx.showLoading({ title: '处理中...' });
api.unbindRespond({ agree: false }).then(function() {
wx.hideLoading();
wx.showToast({ title: '已拒绝解绑请求', icon: 'success' });
self.loadUnbindStatus();
}).catch(function(err) {
wx.hideLoading();
wx.showToast({ title: err.error || '操作失败', icon: 'none' });
});
}
}
});
}
}
});
},
// 导航
goToHistory: function() { wx.navigateTo({ url: '/pages/history/history' }); },
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
goToProfile: function() { wx.navigateBack(); },
});