Files
wechat-clock-mini/pages/course/course.js
王宏豪 ba2d9245ef feat(api): 重构图片地址管理与上传机制
- 替换开发环境API基础URL为本地服务地址
- 实现签名URL本地缓存机制,解决微信按URL缓存图片导致重复下载问题
- 添加缓存清理功能,包括过期清理和按需删除特定缓存项
- 集成阿里云OSS上传功能,支持直传减少服务器带宽占用
- 优化头像地址处理逻辑,统一使用签名缓存避免地址频繁变化
- 移除过时的OSS上传示例文件oss-upload.js
- 更新课程页面实现跳转第三方小程序功能
- 调整打卡记录图片地址获取方式,统一使用新的签名缓存逻辑
- 优化图片上传流程,增加压缩处理和批量上传支持
- 完善认证模块中的缓存清理逻辑,切换账号时清除相关图片缓存
- 添加后端环境变量配置模板和代理文档说明
2026-09-23 17:46:40 +08:00

167 lines
5.7 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.
// 课程中心:全屏 web-view 加载线上课程列表
//
// 注意:课程页部署在 www.muguaclub.cn,与首页 H5 的 m.muguaclub.cn 是不同主机名,
// www 必须单独加入微信公众平台「开发设置 → 业务域名」并在该站点根目录放校验文件,
// 否则真机打开会白屏。
// 登录态:课程页本体是公开的,但产品要求「未登录不进课程页」——未登录时切到「我的」引导登录,
// 因此也不需要走 webview/redirect 换 cookie。
const auth = require('../../utils/auth');
const COURSE_URL = 'https://www.muguaclub.cn/courses/index.html';
// 「去上课」浮窗的目标小程序(第三方,appId 由业务方提供)。
// 只用 navigateToMiniProgram,不传 path → 落对方首页;若将来要直达某个页面,
// 在这里补 path: '/pages/xxx/xxx'(以 / 开头)。
// 白名单:app.json 的 navigateToMiniProgramAppIdList 已于 2020-04-24 取消(配置里若还留着反而报 appJSON 无效),
// 所以不需要任何平台/工程配置。但下面两条绕不过去:
// ① 必须由用户点击触发(基础库 2.3.0 起),所以在 onShow 里自动调会报
// `navigateToMiniProgram:fail can only be invoked by user TAP gesture`;
// ② 跳转前微信一定弹系统确认框,用户取消时 fail.errMsg 含 'cancel'。
const CLASS_MINI_APP_ID = 'wxa3589f1193170aa3';
Page({
data: {
webviewUrl: '',
webviewError: false,
errorMsg: '',
webviewKey: 0,
// 「去上课」浮窗是否已挂载。**故意不跟 web-view 同帧渲染**,见 _scheduleFloat
showFloat: false
},
_webviewKey: 0,
_redirecting: false,
_jumping: false,
_floatTimer: null,
onShow() {
this.ensureAccess();
},
// 未登录不进课程页,直接切到「我的」引导登录
ensureAccess() {
if (auth.getToken()) {
this.loadWebview();
return;
}
// token 可能还在 App.onLaunch 发起的登录流程里,复用同一个登录单例再判一次,避免冷启动误跳
return auth.ensureLogin()
.then((token) => {
if (token) {
this.loadWebview();
return;
}
this.denyAccess();
})
.catch(() => {
this.denyAccess();
});
},
// 清掉可能残留的 web-view(如退出登录后切回本 tab),再切到「我的」
denyAccess() {
clearTimeout(this._floatTimer);
if (this.data.webviewUrl || this.data.showFloat) {
this.setData({ webviewUrl: '', webviewError: false, errorMsg: '', showFloat: false });
}
if (this._redirecting) {
return;
}
this._redirecting = true;
wx.switchTab({
url: '/pages/profile/profile',
complete: () => {
this._redirecting = false;
}
});
},
loadWebview() {
// key 自增 + 时间戳:强制 web-view 销毁重建,避免切 tab 回来白屏或读到上一次的缓存
this._webviewKey += 1;
this.setData({
webviewUrl: COURSE_URL + '?_t=' + Date.now(),
webviewError: false,
errorMsg: '',
webviewKey: this._webviewKey,
// 与上面的字段合并成同一次 setData,别拆开发(同同步树连发会撞 recursive update)
showFloat: false
});
this._scheduleFloat();
},
// 延迟一拍再把浮窗插进去。
//
// 为什么不能跟 web-view 同帧渲染:原生组件按「后插入的盖住先插入的」决定层级,
// 而 web-view 每次 onShow 都因 key 自增被销毁重建 —— 同帧渲染时重建后的 web-view
// 反而压在浮窗上,**真机上是「浮窗不见了」**(安卓默认如此,iOS 多数机型不出现;
// 开发者工具里则是完全看不到覆盖效果,无论怎么改都只能真机验证)。
// 等 web-view 挂载完(250ms)再 setData,浮窗就成了后插入的那一个。
_scheduleFloat() {
clearTimeout(this._floatTimer);
this._floatTimer = setTimeout(() => {
// 定时器可能落在 webviewError / 已切走的场景里,必须再判一次
if (this.data.webviewUrl && !this.data.webviewError) {
this.setData({ showFloat: true });
}
}, 250);
},
onWebviewError(e) {
// 加载失败时把浮窗收掉(错误页上不叠浮窗),同时取消待触发的显示定时器
clearTimeout(this._floatTimer);
this.setData({
webviewError: true,
errorMsg: (e && e.detail && e.detail.errMsg) || '加载失败,请检查网络后重试',
showFloat: false
});
},
onRetry() {
clearTimeout(this._floatTimer);
this.setData({ webviewUrl: '', webviewError: false, errorMsg: '', showFloat: false });
setTimeout(() => {
this.loadWebview();
}, 60);
},
// ---- 去上课:跳转到第三方授课小程序 ----
onGoClass() {
// 防重入:连点会连续弹系统确认框。complete 里复位(跳转成功时本页被挂起,
// 用户从对方小程序返回会走 onShow → 重新加载课程页,这是本页既有行为,不在这里动)
if (this._jumping) {
return;
}
this._jumping = true;
wx.navigateToMiniProgram({
appId: CLASS_MINI_APP_ID,
fail: (err) => {
const msg = (err && err.errMsg) || '';
// 用户点了取消,不是错误
if (msg.indexOf('cancel') >= 0) {
return;
}
wx.showToast({ title: '打开失败,请稍后再试', icon: 'none' });
},
complete: () => {
this._jumping = false;
}
});
},
// ---- 分享 ----
onShareAppMessage() {
return {
title: '明缘云道场 · 全部课程',
path: '/pages/course/course'
};
},
onShareTimeline() {
return {
title: '明缘云道场 · 全部课程',
query: 'from=timeline'
};
}
});