feat(api): 重构图片地址管理与上传机制
- 替换开发环境API基础URL为本地服务地址 - 实现签名URL本地缓存机制,解决微信按URL缓存图片导致重复下载问题 - 添加缓存清理功能,包括过期清理和按需删除特定缓存项 - 集成阿里云OSS上传功能,支持直传减少服务器带宽占用 - 优化头像地址处理逻辑,统一使用签名缓存避免地址频繁变化 - 移除过时的OSS上传示例文件oss-upload.js - 更新课程页面实现跳转第三方小程序功能 - 调整打卡记录图片地址获取方式,统一使用新的签名缓存逻辑 - 优化图片上传流程,增加压缩处理和批量上传支持 - 完善认证模块中的缓存清理逻辑,切换账号时清除相关图片缓存 - 添加后端环境变量配置模板和代理文档说明
This commit is contained in:
@@ -9,16 +9,30 @@ 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
|
||||
webviewKey: 0,
|
||||
// 「去上课」浮窗是否已挂载。**故意不跟 web-view 同帧渲染**,见 _scheduleFloat
|
||||
showFloat: false
|
||||
},
|
||||
|
||||
_webviewKey: 0,
|
||||
_redirecting: false,
|
||||
_jumping: false,
|
||||
_floatTimer: null,
|
||||
|
||||
onShow() {
|
||||
this.ensureAccess();
|
||||
@@ -46,8 +60,9 @@ Page({
|
||||
|
||||
// 清掉可能残留的 web-view(如退出登录后切回本 tab),再切到「我的」
|
||||
denyAccess() {
|
||||
if (this.data.webviewUrl) {
|
||||
this.setData({ webviewUrl: '', webviewError: false, errorMsg: '' });
|
||||
clearTimeout(this._floatTimer);
|
||||
if (this.data.webviewUrl || this.data.showFloat) {
|
||||
this.setData({ webviewUrl: '', webviewError: false, errorMsg: '', showFloat: false });
|
||||
}
|
||||
if (this._redirecting) {
|
||||
return;
|
||||
@@ -68,24 +83,72 @@ Page({
|
||||
webviewUrl: COURSE_URL + '?_t=' + Date.now(),
|
||||
webviewError: false,
|
||||
errorMsg: '',
|
||||
webviewKey: this._webviewKey
|
||||
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) || '加载失败,请检查网络后重试'
|
||||
errorMsg: (e && e.detail && e.detail.errMsg) || '加载失败,请检查网络后重试',
|
||||
showFloat: false
|
||||
});
|
||||
},
|
||||
|
||||
onRetry() {
|
||||
this.setData({ webviewUrl: '', webviewError: false, errorMsg: '' });
|
||||
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 {
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
<view class="page">
|
||||
<web-view src="{{webviewUrl}}" wx:if="{{webviewUrl && !webviewError}}" key="{{webviewKey}}" binderror="onWebviewError"></web-view>
|
||||
|
||||
<!-- 去上课浮窗:web-view 会自动铺满页面并覆盖其他组件(普通 view 加多大 z-index 都盖不住),
|
||||
所以只能用 cover-view —— 它是原生组件,靠「后插入的原生组件覆盖先插入的」生效。
|
||||
⚠️ 因此这个 wx:if 绑定的是 showFloat 而不是 webviewUrl:浮窗**必须比 web-view 晚一步挂载**
|
||||
(见 course.js 的 _scheduleFloat)。web-view 上挂着 key,每次 onShow 都会销毁重建,
|
||||
同帧渲染时重建后的 web-view 反压在浮窗上 —— 真机上表现为「浮窗不见了」。
|
||||
开发者工具里则完全看不到覆盖效果,**只能真机验证**,别在工具里判断它是否生效。
|
||||
⚠️ cover-view 不支持 box-shadow / background-image / 单边 border,样式只能写简单属性,
|
||||
改样式时别加阴影,否则真机上静默不生效。只嵌套 cover-view / cover-image。 -->
|
||||
<cover-view
|
||||
class="class-float"
|
||||
wx:if="{{showFloat}}"
|
||||
hover-class="class-float-hover"
|
||||
bindtap="onGoClass"
|
||||
>
|
||||
<cover-view class="class-float-text">去上课</cover-view>
|
||||
</cover-view>
|
||||
|
||||
<view class="error-page" wx:if="{{webviewError}}">
|
||||
<view class="error-icon">!</view>
|
||||
<view class="error-title">页面加载失败</view>
|
||||
|
||||
@@ -49,3 +49,47 @@
|
||||
.btn-hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ---- 「去上课」浮窗 ----
|
||||
是 cover-view(原生组件),只能用基本定位/布局/文本样式:
|
||||
不支持 box-shadow、background-image、单边 border、overflow: visible。
|
||||
层次靠「主色底 + 浅描边」表达,别改成阴影 —— 真机上会静默不生效。
|
||||
**2026-09-21 起改为「右侧垂直居中」**(原为右下角 bottom: 40rpx)。
|
||||
本页是 tabBar 页面,可视区已不含 tabBar,所以 top: 50% 直接就是「内容区正中」,
|
||||
不需要再为 tabBar 做偏移。
|
||||
**2026-09-21 加大尺寸**(水平 padding 40 → 64 → 80 → **96**;垂直一直是 36,从未变过)。
|
||||
尺寸约 286×112rpx(≈143×56px),满足微信建议的点击热区 ≥ 88rpx。 */
|
||||
.class-float {
|
||||
position: fixed;
|
||||
right: 24rpx;
|
||||
/* 垂直居中 = top 50% + 负 margin 抵消自身高度的一半。
|
||||
⚠️ 不要改成 transform: translateY(-50%) —— cover-view 对 transform 没有官方支持承诺,
|
||||
真机上一旦静默失效,按钮会掉回页面顶部(不是掉到中间,是掉到最上面)。
|
||||
⚠️ 不能同时保留 bottom:position: fixed 下 top/bottom 同时给出且 height 为 auto 时,
|
||||
盒子会被拉伸填满整个视口高度,视觉上变成一条竖条。
|
||||
⚠️ -56rpx 与按钮高度强耦合:高度 = padding 36×2 + 文本 30×1.2(=36) + 边框 2×2 = 112rpx,
|
||||
补偿值恒等于「-高度/2」。**只调水平 padding 不会影响它** —— 想调尺寸又不想碰这个坑时,
|
||||
优先只动水平方向(垂直方向一改就必须重算,否则垂直居中静默偏掉:
|
||||
不是报错,只是看着不太居中,很难肉眼发现)。
|
||||
验证脚本 `verify_course_float.js` 的 [7c] 会按上述算式自动推导并断言,漏改会直接红。
|
||||
⚠️ 宽度同理 = 文本 3字×30(=90) + padding 水平 96×2 + 边框 2×2 = 286rpx(≈屏宽 38%)。
|
||||
按钮越宽,横向压住课程列表的区域越大 —— 真机若发现挡住列表可点元素,优先收窄水平 padding。
|
||||
**要调宽度只需改这一个数字**(水平 padding),不必动其他任何地方。 */
|
||||
top: 50%;
|
||||
margin-top: -56rpx;
|
||||
z-index: 999;
|
||||
padding: 36rpx 96rpx;
|
||||
background: #C67B30;
|
||||
border: 2rpx solid #E8C79A;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
.class-float-text {
|
||||
color: #FFFFFF;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
}
|
||||
.class-float-hover {
|
||||
background: #B06A26;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user