Files
past-go/weapp/pages/storage/storage.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

165 lines
6.3 KiB
JavaScript

const { withRecordAccess } = require('../../utils/record-access');
const { withDataCache } = require('../../utils/page-cache');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { getLayout } = require('../../utils/layout');
const { api } = require('../../utils/request');
function formatBytes(bytes) {
bytes = bytes || 0;
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
if (bytes < 1024 * 1024 * 1024) return (bytes / 1024 / 1024).toFixed(1) + ' MB';
return (bytes / 1024 / 1024 / 1024).toFixed(2) + ' GB';
}
Page(withRecordAccess(withDataCache({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function () { this.setData(getLayout()); },
data: {
loading: true,
error: false,
usedBytes: 0,
usedText: '0 MB',
freeMb: 200,
freeText: '200 MB',
percent: 0,
overQuota: false,
isVip: false,
isUnlimited:false,
quotaText:"200 MB",
vipExpireText: '',
plans: [],
paying: false,
},
onLoad: function () { this._unloaded = false; this.setData(getLayout()); },
onUnload: function () { this._unloaded = true; },
onShow: function () { this.loadInfo(); },
onPullDownRefresh: function () { this.loadInfo().then(function () { wx.stopPullDownRefresh(); }); },
loadInfo: function () {
var self = this;
this.setData({ loading: true, error: false });
return api.storageInfo().then(function (d) {
var freeBytes = d.quota_bytes != null ? d.quota_bytes : (d.quotaBytes != null ? d.quotaBytes : (d.free_bytes || d.freeBytes || 0));
var unlimited=!!(d.is_unlimited || d.isUnlimited);
var usedBytes = d.used_bytes || d.usedBytes || 0;
var percent = freeBytes > 0 ? Math.min(100, Math.round(usedBytes / freeBytes * 100)) : 0;
var overQuota = usedBytes > freeBytes;
var plans = (d.plans || []).map(function (p) {
var cents = p.price_cents || p.priceCents || 0;
return {
key: p.key,
name: p.name,
priceCents:cents,
version:p.updated_at || p.updatedAt,
priceText: (cents / 100).toFixed(2),
unit: (p.months || (p.key==='year'?12:1))+'个月',
description: (Number(p.quota_mb != null ? p.quota_mb : p.quotaMb || 0)===0 ? '无限空间' : formatBytes(Number(p.quota_mb != null ? p.quota_mb : p.quotaMb)*1024*1024))+' · 有效期'+(p.months || (p.key==='year'?12:1))+'个月',
};
});
var vipExpireText = '';
if (d.is_vip || d.isVip) {
var expire = d.vip_expire_at || d.vipExpireAt ? String(d.vip_expire_at || d.vipExpireAt).slice(0, 10) : '';
vipExpireText = expire ? ('有效期至 ' + expire) : '已开通容量套餐';
}
self.setData({
loading: false,
usedBytes: usedBytes,
usedText: formatBytes(usedBytes),
freeMb: d.free_mb != null ? d.free_mb : (d.freeMb != null ? d.freeMb : 200),
freeText: formatBytes(freeBytes),
percent: percent,
overQuota: overQuota,
isVip: !!(d.is_vip || d.isVip),
isUnlimited:unlimited,
quotaText:unlimited?"无限空间":formatBytes(freeBytes),
vipExpireText: vipExpireText,
plans: plans,
});
}).catch(function (e) {
self.setData({ loading: false, error: true });
console.error('加载存储信息失败', e);
});
},
buyPlan: function (e) {
var key = e.currentTarget.dataset.key;
var self = this;
if (this.data.paying) return;
var plan = null;
for (var i = 0; i < this.data.plans.length; i++) {
if (this.data.plans[i].key === key) { plan = this.data.plans[i]; break; }
}
if (!plan) return;
wx.showLoading({ title: '下单中...', mask: true });
this.setData({ paying: true });
api.storageCreateOrder({ plan_key: key, expected_price_cents:plan.priceCents, expected_plan_version:plan.version }).then(function (order) {
wx.hideLoading();
if (order && order.pay_provider === 'wechat' && order.payment) {
self.callWechatPay(order);
return;
}
self.setData({ paying: false });
wx.showToast({ title: '支付参数不完整,请重新下单', icon: 'none' });
}).catch(function (err) {
wx.hideLoading();
self.setData({ paying: false });
wx.showToast({ title: (err && err.error) || '下单失败', icon: 'none' });
});
},
callWechatPay: function (order) {
var self = this;
var payment = order.payment;
if (!wx.requestPayment) {
this.setData({ paying: false });
wx.showToast({ title: '当前环境不支持微信支付,请使用微信真机', icon: 'none' });
return;
}
wx.requestPayment({
timeStamp: payment.time_stamp,
nonceStr: payment.nonce_str,
package: payment.package,
signType: payment.sign_type,
paySign: payment.pay_sign,
success: function () { return self.confirmWechatPayment(order.out_trade_no); },
fail: function (error) {
// Even a cashier error can occur after payment; query the server once.
var message = String(error && error.errMsg || '').indexOf('cancel') >= 0 ? '已取消支付' : '支付未完成';
return self.confirmWechatPayment(order.out_trade_no, message);
}
});
},
confirmWechatPayment: async function (number, failureMessage) {
if (this._confirming) return;
this._confirming = true;
try {
for (var attempt = 0; attempt < (failureMessage ? 1 : 5); attempt++) {
var result = await api.storageOrderStatus(number);
if (this._unloaded) return;
if (result.status === 'success') {
wx.showToast({ title: '支付成功,会员已开通', icon: 'success' });
await this.loadInfo();
return;
}
if (result.status === 'closed') {
wx.showToast({ title: '订单已关闭,请重新下单', icon: 'none' });
return;
}
if (!failureMessage && attempt < 4) await new Promise(function (resolve) { setTimeout(resolve, 1200); });
}
if (!this._unloaded) wx.showToast({ title: failureMessage || '支付结果确认中,请稍后刷新', icon: 'none' });
} catch (e) {
if (!this._unloaded) wx.showToast({ title: '支付结果暂未确认,请稍后刷新', icon: 'none' });
} finally {
this._confirming = false;
if (!this._unloaded) this.setData({ paying: false });
}
},
}, {loadInfo:['storageInfo']})));