4 Commits
Author SHA1 Message Date
cao123 b70f00fb53 fix 2026-09-07 16:50:54 +08:00
cao123 bb28b8e719 fix 2026-09-07 16:35:36 +08:00
cao123 78bc9e3c86 fix 2026-09-07 09:03:11 +08:00
cao123 de47ffc773 vod视频播放 2026-09-03 16:14:47 +08:00
21 changed files with 384 additions and 749 deletions
+6 -6
View File
@@ -2,7 +2,6 @@
"pages": [ "pages": [
"pages/tab-bar/medicine-box/index", "pages/tab-bar/medicine-box/index",
"pages/tab-bar/medicine-box/add/index", "pages/tab-bar/medicine-box/add/index",
"pages/tab-bar/drug-wiki/index",
"pages/tab-bar/education/index", "pages/tab-bar/education/index",
"pages/tab-bar/profile/index", "pages/tab-bar/profile/index",
"pages/tab-bar/profile/edit/index", "pages/tab-bar/profile/edit/index",
@@ -14,6 +13,12 @@
"pages/live/report/report", "pages/live/report/report",
"pages/common/red/red" "pages/common/red/red"
], ],
"plugins": {
"myPlugin": {
"version": "0.1.5",
"provider": "wx116d0dd5e6a39ac7"
}
},
"tabBar": { "tabBar": {
"custom": true, "custom": true,
"list": [ "list": [
@@ -21,10 +26,6 @@
"pagePath": "pages/tab-bar/medicine-box/index", "pagePath": "pages/tab-bar/medicine-box/index",
"text": "电子药箱" "text": "电子药箱"
}, },
{
"pagePath": "pages/tab-bar/drug-wiki/index",
"text": "药品百科"
},
{ {
"pagePath": "pages/tab-bar/education/index", "pagePath": "pages/tab-bar/education/index",
"text": "惠教中心" "text": "惠教中心"
@@ -49,7 +50,6 @@
"sdkVersionEnd": "15.255.255" "sdkVersionEnd": "15.255.255"
} }
}, },
"componentFramework": "glass-easel",
"sitemapLocation": "sitemap.json", "sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents", "lazyCodeLoading": "requiredComponents",
"requiredPrivateInfos": [ "requiredPrivateInfos": [
-5
View File
@@ -7,11 +7,6 @@ Component({
text: '电子药箱', text: '电子药箱',
icon: '💊', icon: '💊',
}, },
{
pagePath: 'pages/tab-bar/drug-wiki/index',
text: '药品百科',
icon: '📖',
},
{ {
pagePath: 'pages/tab-bar/education/index', pagePath: 'pages/tab-bar/education/index',
text: '惠教中心', text: '惠教中心',
-5
View File
@@ -1,5 +0,0 @@
{
"disableScroll": true,
"navigationBarBackgroundColor": "#FFFFFF",
"usingComponents": {}
}
-112
View File
@@ -1,112 +0,0 @@
/* pages/live/h5Room/index.wxss */
page {
height: 100%;
background: #eaf6ff;
overflow: hidden;
}
.h5-room {
width: 100%;
min-height: 100vh;
background: #fff;
padding: 0;
box-sizing: border-box;
}
.h5-room__nav {
position: relative;
width: 100%;
background: linear-gradient(135deg, #edf8ff 0%, #dbeeff 48%, #cae5ff 100%);
box-sizing: border-box;
overflow: hidden;
box-shadow: inset 0 -1px 0 rgba(120, 180, 255, 0.14);
}
.h5-room__nav-status {
position: relative;
z-index: 2;
width: 100%;
background: transparent;
}
.h5-room__nav-bg,
.h5-room__nav-grid,
.h5-room__nav-glow,
.h5-room__nav-bubble {
position: absolute;
}
.h5-room__nav-bg {
top: 0;
right: 0;
bottom: 0;
left: 0;
background:
radial-gradient(circle at 14% 82%, rgba(74, 183, 255, 0.26) 0%, rgba(74, 183, 255, 0) 28%),
radial-gradient(circle at 88% 22%, rgba(93, 255, 240, 0.22) 0%, rgba(93, 255, 240, 0) 20%),
radial-gradient(circle at 76% 84%, rgba(123, 152, 255, 0.18) 0%, rgba(123, 152, 255, 0) 24%),
linear-gradient(135deg, #edf8ff 0%, #dbeeff 48%, #cae5ff 100%);
}
.h5-room__nav-grid {
top: 0;
right: 0;
bottom: 0;
left: 0;
opacity: 0.42;
background-image:
linear-gradient(rgba(255, 255, 255, 0.32) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.28) 1px, transparent 1px);
background-size: 18px 18px;
background-position: center center;
}
.h5-room__nav-glow {
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.28);
}
.h5-room__nav-glow--left {
top: 20rpx;
left: -36rpx;
width: 220rpx;
height: 220rpx;
}
.h5-room__nav-glow--right {
right: -30rpx;
bottom: -72rpx;
width: 260rpx;
height: 260rpx;
background: rgba(147, 177, 255, 0.2);
}
.h5-room__nav-bubble {
border-radius: 50%;
background: rgba(71, 170, 255, 0.5);
box-shadow: 0 0 0 10rpx rgba(255, 255, 255, 0.12);
}
.h5-room__nav-bubble--sm {
top: 22rpx;
right: 184rpx;
width: 12rpx;
height: 12rpx;
background: rgba(89, 187, 255, 0.8);
}
.h5-room__nav-bubble--md {
top: 66rpx;
left: 112rpx;
width: 18rpx;
height: 18rpx;
background: rgba(79, 165, 255, 0.58);
}
.h5-room__nav-bubble--lg {
right: 64rpx;
bottom: 18rpx;
width: 28rpx;
height: 28rpx;
background: rgba(120, 157, 255, 0.38);
}
-108
View File
@@ -1,108 +0,0 @@
// pages/live/h5Room/index.ts
import { appId } from "../../../env";
Page({
/**
* 页面的初始数据
*/
data: {
webViewUrl: "",
webViewHeight: 0,
statusBarHeight: 0,
navBarHeight: 0,
},
async onLoad(options: any) {
const windowInfo = (wx as any).getWindowInfo
? (wx as any).getWindowInfo()
: wx.getSystemInfoSync();
const menuButtonRect = wx.getMenuButtonBoundingClientRect();
const statusBarHeight =
windowInfo.safeArea?.top || windowInfo.statusBarHeight || 0;
const navBarHeight = menuButtonRect?.top
? menuButtonRect.bottom + menuButtonRect.top - statusBarHeight
: statusBarHeight + 44;
const webViewHeight = Math.max(
(windowInfo.windowHeight || 0) - navBarHeight,
0,
);
this.setData({
statusBarHeight,
navBarHeight,
webViewHeight,
});
const app = getApp<IAppOption>();
const bool = await app.login("room");
if (!bool) return;
try {
// const baseUrl = "http://localhost:5173/pages/room";
const baseUrl = "https://live.byteoc.com/1859506470730939/1425285?platform=mobile";
const openId = wx.getStorageSync("openId");
const roomId = options?.roomId;
const params: Record<string, string> = {};
if (openId) params.openId = openId;
if (appId) params.appId = appId;
if (roomId) params.roomId = roomId;
if (options?.extraParams) {
try {
const extraParams = JSON.parse(
decodeURIComponent(options.extraParams),
);
Object.entries(extraParams).forEach(([key, value]) => {
params[key] = String(value);
});
} catch (e) {
console.warn("Failed to parse extra parameters");
}
}
const queryString = Object.entries(params)
.map(
([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`,
)
.join("&");
const finalUrl = `${baseUrl}?${queryString}`;
this.setData({
webViewUrl: finalUrl,
});
} catch (error) {
console.error("Error generating URL:", error);
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {},
/**
* 生命周期函数--监听页面显示
*/
onShow() {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {},
});
-2
View File
@@ -1,2 +0,0 @@
<!--pages/live/h5Room/index.wxml-->
<text>pages/live/h5Room/index.wxml</text>
@@ -1,5 +1,5 @@
import UrlParamsHandler from "../../../utils/store"; import UrlParamsHandler from "../../../utils/store";
import { appversion, version } from "../../../env"; import { version } from "../../../env";
import { request } from "../../../utils/request"; import { request } from "../../../utils/request";
const defaultAvatarUrl = const defaultAvatarUrl =
"https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0"; "https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0";
@@ -45,7 +45,7 @@ Page({
}; };
const data = await request({ const data = await request({
options: { options: {
url: `/app/video-watch/details?version=${version.details}&appVer=${appversion}`, url: `/app/video-watch/details`,
method: "GET", method: "GET",
data: params, data: params,
}, },
@@ -110,35 +110,35 @@ Page({
/** /**
* 生命周期函数--监听页面初次渲染完成 * 生命周期函数--监听页面初次渲染完成
*/ */
onReady() {}, onReady() { },
/** /**
* 生命周期函数--监听页面显示 * 生命周期函数--监听页面显示
*/ */
onShow() {}, onShow() { },
/** /**
* 生命周期函数--监听页面隐藏 * 生命周期函数--监听页面隐藏
*/ */
onHide() {}, onHide() { },
/** /**
* 生命周期函数--监听页面卸载 * 生命周期函数--监听页面卸载
*/ */
onUnload() {}, onUnload() { },
/** /**
* 页面相关事件处理函数--监听用户下拉动作 * 页面相关事件处理函数--监听用户下拉动作
*/ */
onPullDownRefresh() {}, onPullDownRefresh() { },
/** /**
* 页面上拉触底事件的处理函数 * 页面上拉触底事件的处理函数
*/ */
onReachBottom() {}, onReachBottom() { },
/** /**
* 用户点击右上角分享 * 用户点击右上角分享
*/ */
onShareAppMessage() {}, onShareAppMessage() { },
}); });
-9
View File
@@ -1,9 +0,0 @@
{
"backgroundTextStyle": "light",
"backgroundColor": "#ffffff",
"navigationBarShareAppMessage": false,
"navigationBarShareTimeline": false,
"usingComponents": {
"liveroom": "/components/liveroom/liveroom"
}
}
-6
View File
@@ -1,6 +0,0 @@
/* pages/live/room/room.wxss */
@import "../../../variables";
page {
background: #f9f9f9;
height: 100%;
}
-75
View File
@@ -1,75 +0,0 @@
// pages/live/room/room.ts
Page({
/**
* 页面的初始数据
*/
data: {
joinedGroupId: "", // 已加入的消息组id
windowHeight: "100%",
windowWidth: "100%",
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
const app = getApp<IAppOption>();
wx.setStorageSync("joinedGroupId", options.groupId);
const bool = await app.login("room");
if (!bool) return;
this.setData({
joinedGroupId: options.groupId,
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {},
/**
* 生命周期函数--监听页面显示
*/
onShow() {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
const groupId = this.data.joinedGroupId || wx.getStorageSync("joinedGroupId");
const path = groupId
? `/pages/live/room/room?groupId=${groupId}`
: "/pages/live/room/room";
return {
/*
title: "邀请你进入直播间",
*/
title: "邀请你进入直播间",
path,
imageUrl: "/assets/img/600.jpg",
};
},
});
-4
View File
@@ -1,4 +0,0 @@
<!--pages/live/room/room.wxml-->
<view style="width: {{windowWidth}}; height: {{windowHeight}};">
<liveroom joined-group-id="{{joinedGroupId}}" />
</view>
+3 -2
View File
@@ -6,9 +6,10 @@
"disableScroll": true, "disableScroll": true,
"usingComponents": { "usingComponents": {
"navigation-bar": "/components/navigation-bar/navigation-bar", "navigation-bar": "/components/navigation-bar/navigation-bar",
"my-video":"/components/my-video/my-video", "my-video": "/components/my-video/my-video",
"van-tab": "@vant/weapp/tab/index", "van-tab": "@vant/weapp/tab/index",
"van-tabs": "@vant/weapp/tabs/index", "van-tabs": "@vant/weapp/tabs/index",
"my-quiz":"/components/my-quiz/my-quiz" "my-quiz":"/components/my-quiz/my-quiz",
"my-player": "plugin://myPlugin/player"
} }
} }
+9
View File
@@ -142,3 +142,12 @@ rich-content img {
height: 80rpx; height: 80rpx;
} }
} }
.cover {
position: absolute;
bottom: 0;
z-index: 998;
height: 20%;
width: 70%;
left: 10%;
background-color: rgba(255, 255, 255, 0.007);
}
+306 -306
View File
@@ -3,8 +3,9 @@
import UrlParamsHandler from "../../../utils/store"; import UrlParamsHandler from "../../../utils/store";
import { htmlToWxNodes } from "../../../utils/html-to-wx-nodes"; import { htmlToWxNodes } from "../../../utils/html-to-wx-nodes";
import { request } from "../../../utils/request"; import { request } from "../../../utils/request";
import { appId } from "../../../env"; import { AES_IV, AES_KEY, appId } from "../../../env";
import { receiveRedPacket } from "../../../utils/util"; import { receiveRedPacket } from "../../../utils/util";
import { aesEncrypt } from "../../../utils/crypto";
const isMobileEnvironment = (): boolean => { const isMobileEnvironment = (): boolean => {
const platform = wx.getSystemInfoSync().platform?.toLowerCase() || ''; const platform = wx.getSystemInfoSync().platform?.toLowerCase() || '';
@@ -12,259 +13,311 @@ const isMobileEnvironment = (): boolean => {
}; };
Page({ Page({
/** // 页面私有变量,避免触发无效渲染
* 页面的初始数据 _wasPlayingBeforeInterrupt: false,
*/ _pauseVideoHandler: null as any,
_resumeVideoHandler: null as any,
_appHideHandler: null as any,
_appShowHandler: null as any,
_networkStatusChangeHandler: null as any,
_videoContext: null as any,
data: { data: {
dataList: {}, dataList: {} as any,
nodes: [] as any[], nodes: [] as any[],
eveId: null, eveId: null,
store: {}, store: {},
fileid: "",
active: 0, active: 0,
canSubmit: false, canSubmit: false,
safeBottom: 0, safeBottom: 24,
resumeTime: 0,
playing: false,
currentTime: 0,
sendDurationTimer: 0,
isAppHidden: false,
}, },
/**
* 生命周期函数--监听页面加载
*/
async onLoad(options: any) { async onLoad(options: any) {
const app = getApp<IAppOption>(); const app = getApp<IAppOption>();
wx.hideShareMenu({ wx.hideShareMenu({ menus: ["shareAppMessage"] });
menus: ["shareAppMessage"], // 单独禁用好友分享
});
new UrlParamsHandler(options); new UrlParamsHandler(options);
app.globalData.store = options; app.globalData.store = options;
let safeBottom = 0;
//@ts-ignore // 适配底部安全区
if (wx.getWindowInfo) { let safeBottom = 24;
try {
//@ts-ignore //@ts-ignore
const systemInfo = wx.getWindowInfo(); const { windowHeight, safeArea } = wx.getWindowInfo ? wx.getWindowInfo() : wx.getSystemInfoSync();
safeBottom = safeBottom = safeArea ? Math.abs(windowHeight - safeArea.bottom) + 10 : 24;
Math.abs(systemInfo.windowHeight - systemInfo.safeArea.bottom) + 10; } catch (e) { }
if (safeBottom === 0) safeBottom = 24;
} else { this.setData({ safeBottom, store: options });
const systemInfo = wx.getSystemInfoSync(); this.initSystemListeners();
safeBottom = systemInfo.safeArea
? Math.abs(systemInfo.windowHeight - systemInfo.safeArea.bottom) + 10
: 24;
}
this.setData({
safeBottom: safeBottom,
store: options,
});
if (!isMobileEnvironment()) { if (!isMobileEnvironment()) {
this.handleEnterError( return this.handleEnterError("当前环境不支持观看,请使用手机观看。", "环境不支持");
"当前环境不支持观看,请使用手机观看。",
"环境不支持",
);
return;
} }
// 开启防截屏(仅限当前页面生命周期内) if (!(await app.login("video"))) return;
//@ts-ignore
wx.setVisualEffectOnCapture({ //@ts-ignore 开启防截屏
visualEffect: 'hidden', if (wx.setVisualEffectOnCapture) wx.setVisualEffectOnCapture({ visualEffect: 'hidden' });
});
}, },
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() { },
handleEnterError(message: string, title = "无法进入") {
wx.showModal({
title: title,
content: message,
showCancel: false,
confirmText: "确定",
});
},
handleBack() {
wx.switchTab({
url: "/pages/tab-bar/education/index",
});
},
/**
* 生命周期函数--监听页面显示
*/
onShow() { onShow() {
this.search(); this.search();
}, },
/**
* 生命周期函数--监听页面隐藏
*/
onHide() { onHide() {
// 离开视频页时恢复截屏能力,避免影响其他页面
//@ts-ignore //@ts-ignore
wx.setVisualEffectOnCapture({ if (wx.setVisualEffectOnCapture) wx.setVisualEffectOnCapture({ visualEffect: 'none' });
visualEffect: 'none', this.flushProgress();
});
}, },
/**
* 生命周期函数--监听页面卸载
*/
onUnload() { onUnload() {
// 卸载时恢复截屏能力
//@ts-ignore //@ts-ignore
wx.setVisualEffectOnCapture({ if (wx.setVisualEffectOnCapture) wx.setVisualEffectOnCapture({ visualEffect: 'none' });
visualEffect: 'none', this.flushProgress();
});
try { this.pauseVideoBeforeNavigate(); } catch (err) { }
// 卸载事件与定时器
if (this._pauseVideoHandler) wx.offAudioInterruptionBegin(this._pauseVideoHandler);
if (this._resumeVideoHandler) wx.offAudioInterruptionEnd(this._resumeVideoHandler);
if (this._appHideHandler) wx.offAppHide(this._appHideHandler);
if (this._appShowHandler) wx.offAppShow(this._appShowHandler);
if (this._networkStatusChangeHandler) wx.offNetworkStatusChange(this._networkStatusChangeHandler);
this.clearSendProgressTimer();
}, },
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() { },
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() { },
/**
* 用户点击右上角分享
*/
onShareAppMessage() { },
onChange(event: any) { onChange(event: any) {
this.setData({ this.setData({ active: event.detail.index });
active: event.detail.index, },
initSystemListeners() {
this._networkStatusChangeHandler = (res: any) => {
if (!res.isConnected) {
wx.showToast({ title: '网络连接断开', icon: 'none' });
this.pauseVideoBeforeNavigate();
} else if (res.networkType !== 'wifi') {
wx.showToast({ title: '当前为非Wi-Fi环境,请注意流量消耗', icon: 'none' });
}
};
wx.onNetworkStatusChange(this._networkStatusChangeHandler);
this._pauseVideoHandler = () => {
this.flushProgress();
this._wasPlayingBeforeInterrupt = this.data.playing;
this.pauseVideoBeforeNavigate();
};
this._resumeVideoHandler = () => {
if (this._wasPlayingBeforeInterrupt && !this.data.isAppHidden) {
this.resumeVideoAfterInterrupt();
}
this._wasPlayingBeforeInterrupt = false;
};
this._appHideHandler = () => {
this.flushProgress();
this.setData({ isAppHidden: true });
this.pauseVideoBeforeNavigate();
};
this._appShowHandler = () => {
this.setData({ isAppHidden: false });
};
wx.onAudioInterruptionBegin(this._pauseVideoHandler);
wx.onAudioInterruptionEnd(this._resumeVideoHandler);
wx.onAppHide(this._appHideHandler);
wx.onAppShow(this._appShowHandler);
},
getVideoContext() {
if (!this._videoContext) {
this._videoContext = wx.createVideoContext("myPlayerId", this);
}
return this._videoContext;
},
pauseVideoBeforeNavigate() {
this.getVideoContext().pause();
},
resumeVideoAfterInterrupt() {
this.getVideoContext().play();
},
// 统一进度落盘(极简版,直接信任内存里的 currentTime)
flushProgress() {
const t = this.data.currentTime;
if (t > 0) {
wx.setStorageSync("interruptedTime", t);
wx.setStorageSync("currentTime", t);
}
},
getResumeTime(): number {
if (wx.getStorageSync("ended")) return 0;
const interrupted = Number(wx.getStorageSync("interruptedTime") || 0);
const current = Number(wx.getStorageSync("currentTime") || 0);
return Math.max(interrupted, current);
},
onPlayerPlay() {
this.setData({ playing: true });
// 异步拉取到 fileid 后,初次播放如果存在历史记录,则跳转
if (this.data.resumeTime > 0) {
this.getVideoContext().seek(this.data.resumeTime);
this.setData({ resumeTime: 0 }); // 跳转完立即清空,防止二次跳转
}
this.startSendProgress();
},
onPlayerPause() {
this.setData({ playing: false });
this.clearSendProgressTimer();
this.flushProgress();
},
onPlayerEnded() {
this.setData({ playing: false, resumeTime: 0, currentTime: 0 });
wx.setStorageSync("ended", true);
this.clearSendProgressTimer();
wx.removeStorageSync("interruptedTime");
wx.removeStorageSync("currentTime");
},
onPlayerError(e: any) {
const detail = e?.detail || e || {};
wx.showModal({
title: "播放出错",
content: String(detail.errMsg || detail.message || JSON.stringify(detail) + ',请联系客服'),
showCancel: false,
}); });
}, },
// 监听答题变化 onPlayerTimeUpdate(e: any) {
onAnswerChange() { let time = e.detail?.currentTime ?? e.detail?.detail?.currentTime;
this.updateProgress(); if (typeof time === 'number' && time > 0) {
const currentTime = Math.floor(time);
if (currentTime === this.data.currentTime) return; // 避免重复操作
this.data.currentTime = currentTime;
// 逢 5 秒落盘一次
if (currentTime % 5 === 0) this.flushProgress();
}
},
startSendProgress() {
this.clearSendProgressTimer();
this.sendProgress(); // 启动时先发一次
const timer = setInterval(() => this.sendProgress(), 15000) as unknown as number;
this.setData({ sendDurationTimer: timer });
},
clearSendProgressTimer() {
if (this.data.sendDurationTimer) {
clearInterval(this.data.sendDurationTimer);
this.setData({ sendDurationTimer: 0 });
}
},
async sendProgress() {
if (!this.data.playing) return;
try {
const code = wx.getStorageSync("code");
const openId = wx.getStorageSync("openId");
const duration = this.data.currentTime + 1; // 时长兜底补偿
const params = code
? { openId, code, duration }
: { openId, state: wx.getStorageSync("state"), seeId: wx.getStorageSync("seeId"), duration };
await request({
options: {
url: code ? `/app/video-watch/inspect` : `/app/watch/inspect`,
data: aesEncrypt(JSON.stringify(params), AES_KEY, AES_IV),
},
isLoading: false,
});
} catch (err) {
console.error("上报观看进度失败:", err);
}
}, },
async search() { async search() {
const app = getApp<IAppOption>(); const app = getApp<IAppOption>();
if (!(await app.checkLoginState())) return;
const code = wx.getStorageSync("code"); const code = wx.getStorageSync("code");
const params = { const params = { openId: wx.getStorageSync("openId"), ...app.globalData.store };
openId: wx.getStorageSync("openId"),
...app.globalData.store,
};
if (code) {
const data = await request({
options: {
url: `/app/video-watch/simple-details`,
method: "GET",
data: params,
},
});
if (!data.watch.url) { const data = await request({
wx.reLaunch({ options: {
url: `/pages/live/registration/registration?content=${data.watch.content}&state=${-1}`, url: code ? `/app/video-watch/details` : `/app/watch/details`,
}); method: code ? "GET" : "POST",
} data: params
const nodes = htmlToWxNodes(data.watch.content); },
this.setData({ });
dataList: data.watch,
eveId: data.watch.eveId,
nodes,
});
} else {
const data = await request({
options: {
url: `/app/watch/simple-details`,
data: params,
},
});
if (!data.data.url) { const targetData = code ? data.watch : data.data;
wx.reLaunch({
url: `/pages/live/registration/registration?content=${data.data.content if (!targetData?.extend?.fileid) {
}&state=${5}`, const stateParam = code ? -1 : 5;
}); return wx.reLaunch({
} url: `/pages/live/registration/registration?content=${targetData?.content || ''}&state=${stateParam}`,
const nodes = htmlToWxNodes(data.data.content);
this.setData({
dataList: data.data,
eveId: data.data.eveId,
nodes,
}); });
} }
this.selectComponent("#tabs").resize(); this.setData({
dataList: targetData,
eveId: targetData.eveId,
nodes: htmlToWxNodes(targetData.content),
fileid: targetData.extend.fileid,
resumeTime: this.getResumeTime(), // 获取到真实视频时,一次性读取恢复点
});
this.selectComponent("#tabs")?.resize();
}, },
// 更新答题进度
updateProgress() { onAnswerChange() {
const quizComponent = this.selectComponent("#quizComponent"); const quizComponent = this.selectComponent("#quizComponent");
if (quizComponent) { if (quizComponent) {
const canSubmit = quizComponent.isAllAnswered(); this.setData({ canSubmit: quizComponent.isAllAnswered() });
this.setData({
canSubmit: canSubmit,
});
} }
}, },
handleSubmit(this) { handleSubmit() {
const ended = wx.getStorageSync("ended") || false; if (!wx.getStorageSync("ended")) {
if (!ended) { return wx.showModal({ title: "提示", content: "请先观看完整视频,再参与答题", showCancel: false, confirmColor: "#FF0000" });
wx.showModal({
title: "请先观看视频,再答题",
showCancel: false,
confirmText: "确定",
confirmColor: "#FF0000",
});
return;
} }
//@ts-ignore
if (this.data.dataList?.userAnsNum >= this.data.dataList?.answerMax) { if (this.data.dataList?.userAnsNum >= this.data.dataList?.answerMax) {
wx.showToast({ return wx.showToast({ title: "答题次数已用完", icon: "none", duration: 3000 });
title: "答题次数已用完",
icon: "none",
duration: 6000,
});
return;
} }
const quizComponent = this.selectComponent("#quizComponent"); const quizComponent = this.selectComponent("#quizComponent");
if (!quizComponent) return wx.showToast({ title: "组件加载失败", icon: "none" });
if (!quizComponent) { if (!quizComponent.isAllAnswered()) {
wx.showToast({ const { answered, total } = quizComponent.getProgress();
title: "组件加载失败", return wx.showModal({
icon: "none",
});
return;
}
const isAllAnswered = quizComponent.isAllAnswered();
if (!isAllAnswered) {
const progress = quizComponent.getProgress();
wx.showModal({
title: "答题未完成", title: "答题未完成",
content: `您已完成 ${progress.answered}/${progress.total} 题,请完成所有题目后再提交。`, content: `您已完成 ${answered}/${total} 题,请完成所有题目后再提交。`,
showCancel: false, showCancel: false,
confirmText: "继续答题",
}); });
return;
} }
const answers = quizComponent.getAllAnswers(); const answers = quizComponent.getAllAnswers();
if (!answers || answers.length === 0) { if (!answers?.length) return wx.showToast({ title: "请先完成答题", icon: "none" });
wx.showToast({
title: "请先完成答题",
icon: "none",
});
return;
}
wx.showModal({ wx.showModal({
title: "确认提交", title: "确认提交",
content: `您已完成 ${answers.length} 道题目,确定要提交答案吗?`, content: `您已完成 ${answers.length} 道题目,确定要提交答案吗?`,
success: (res) => { success: (res) => { if (res.confirm) this.submitAnswers(answers); },
if (res.confirm) {
this.submitAnswers(answers);
}
},
}); });
}, },
@@ -276,145 +329,92 @@ Page({
eveId: this.data.eveId, eveId: this.data.eveId,
...this.data.store, ...this.data.store,
}; };
wx.showLoading({
title: "提交中...",
});
let res = null;
if (code) { wx.showLoading({ title: "提交中..." });
res = await request({ const url = code ? "/app/video-watch/interactive" : "/app/watch/interactive";
options: { const res = await request({ options: { url, data: params } });
url: "/app/video-watch/interactive", wx.hideLoading();
data: params,
if (res.code !== 200) {
return wx.showToast({ title: res.msg || "提交失败", icon: "none", duration: 2000 });
}
const { pass, redPackage } = res.data || {};
if (pass) {
wx.showModal({
content: "🎉 回答正确,领取红包!",
showCancel: false,
confirmText: "立即领取",
confirmColor: "#FF0000",
success: async (res) => {
if (!res.confirm) return;
if (redPackage?.packageInfo) {
this.transferRedPacket(redPackage);
} else if (redPackage?.redId) {
await this.handleReceive(redPackage.redId);
}
}, },
}); });
} else { } else {
res = await request({ wx.showToast({ title: "回答错误,与红包擦肩而过", icon: "none", duration: 3000 });
options: {
url: "/app/watch/interactive",
data: params,
},
});
} }
if (res.code !== 200) {
wx.showToast({
title: res.msg || "提交失败",
icon: "none",
duration: 2000,
});
return;
}
const { pass } = res.data || {};
if (pass) {
wx.showToast({
title: "提交成功",
icon: "success",
duration: 2000,
});
}
// if (pass) {
// wx.showModal({
// content: "🎉 领取红包!",
// showCancel: false,
// confirmText: "立即领取",
// confirmColor: "#FF0000",
// success: async (res) => {
// if (res.confirm) {
// if (redPackage?.packageInfo) {
// try {
// wx.showLoading({ title: "领取中", mask: true });
// if (wx.canIUse?.("requestMerchantTransfer")) {
// await new Promise<void>((resolve, reject) => {
// // @ts-ignore
// wx.requestMerchantTransfer({
// mchId: redPackage.mchId,
// appId,
// package: redPackage.packageInfo,
// success: () => {
// wx.showToast({ title: "领取成功", icon: "success" });
// resolve();
// },
// fail: (err: any) => {
// console.error("拉起转账失败:", err);
// wx.showToast({ title: "领取失败", icon: "none" });
// reject(new Error(err?.errMsg || "领取失败"));
// },
// });
// });
// } else {
// wx.showModal({
// content: "你的微信版本过低,请更新至最新版本。",
// showCancel: false,
// });
// throw new Error("微信版本过低");
// }
// } catch (err: any) {
// wx.hideLoading();
// const msg = err?.data?.msg || err?.errMsg || err?.message || "领取失败";
// wx.showToast({ title: msg, icon: "none" });
// }
// } else {
// await this.handleReceive(redPackage.redId);
// }
// }
// },
// });
// } else {
// wx.showToast({
// title: "回答错误,您与红包擦肩而过",
// icon: "none",
// duration: 6000,
// });
// }
}, },
async handleReceive(id: any) {
const { code } = this.data.store as Record<string, any>; async transferRedPacket(redPackage: any) {
const params = { wx.showLoading({ title: "领取中", mask: true });
appId, if (!wx.canIUse?.("requestMerchantTransfer")) {
code, wx.hideLoading();
openId: wx.getStorageSync("openId"), return wx.showModal({ content: "你的微信版本过低,请更新至最新版本。", showCancel: false });
redId: id,
} }
try { try {
await new Promise<void>((resolve, reject) => {
//@ts-ignore
wx.requestMerchantTransfer({
mchId: redPackage.mchId,
appId,
package: redPackage.packageInfo,
success: () => resolve(),
fail: (err: any) => reject(new Error(err?.errMsg || "领取失败")),
});
});
wx.hideLoading();
wx.showToast({ title: "领取成功", icon: "success" });
} catch (err: any) {
wx.hideLoading();
wx.showToast({ title: err?.message || "领取失败", icon: "none" });
}
},
async handleReceive(id: any) {
try {
wx.showLoading({ title: "领取中" });
await receiveRedPacket( await receiveRedPacket(
params, { appId, code: wx.getStorageSync("code"), openId: wx.getStorageSync("openId"), redId: id },
'/app/general/receive-red', '/app/general/receive-red',
appId appId
); );
wx.hideLoading();
} catch (err) { } catch (err) {
console.error('领取红包失败:', err); wx.hideLoading();
} }
}, },
// 跳转前暂停视频播放
pauseVideoBeforeNavigate() { handleEnterError(message: string, title = "无法进入") {
const video = this.selectComponent("#myVideo") as any; wx.showModal({ title, content: message, showCancel: false });
if (video && video.pauseVideo) { },
video.pauseVideo();
} handleBack() {
wx.switchTab({ url: "/pages/tab-bar/course/course" });
}, },
handleCustomReport() { handleCustomReport() {
this.pauseVideoBeforeNavigate(); this.pauseVideoBeforeNavigate();
const { code, state, seeId } = this.data.store as Record<string, any>; wx.navigateTo({ url: `/pages/live/report/report` });
const params: Record<string, string> = {};
if (code) params.code = code;
if (state) params.state = state;
if (seeId) params.seeId = seeId;
wx.navigateTo({
url: `/pages/live/report/report`,
});
}, },
handleViewHitory() { handleViewHitory() {
this.pauseVideoBeforeNavigate(); this.pauseVideoBeforeNavigate();
const { code, state, seeId } = this.data.store as Record<string, any>; wx.navigateTo({ url: `/pages/live/red-history/red-history` });
const params: Record<string, string> = {};
if (code) params.code = code;
if (state) params.state = state;
if (seeId) params.seeId = seeId;
wx.navigateTo({
url: `/pages/live/red-history/red-history`,
});
} }
}); });
+21 -4
View File
@@ -9,8 +9,25 @@
<view style="display: flex; align-items: center; padding: 0 20rpx; height: 70rpx; background: #FFF7E6;"> <view style="display: flex; align-items: center; padding: 0 20rpx; height: 70rpx; background: #FFF7E6;">
<view style="font-size: 22rpx; color: #999;">本视频仅用于科普,无任何恶意引导行为</view> <view style="font-size: 22rpx; color: #999;">本视频仅用于科普,无任何恶意引导行为</view>
</view> </view>
<my-video id="myVideo" src="{{dataList.url}}" backgroundUrl="{{dataList.background}}"></my-video> <!-- VOD 云点播短视频插件方案:续播 seek / 进度缓存上报 / 电话中断恢复 / 全屏状态同步 -->
<my-player
width="100%"
height="210px"
appid="1304584727"
fileid="{{fileid}}"
initial-time="{{resumeTime}}"
enable-progress-gesture="{{false}}"
playerid="myPlayerId"
bindplay="onPlayerPlay"
bindpause="onPlayerPause"
bindended="onPlayerEnded"
binderror="onPlayerError"
bindtimeupdate="onPlayerTimeUpdate"
bindloadedmetadata="onPlayerLoadedMetadata"
show-fullscreen-btn
>
<cover-view class="cover"></cover-view>
</my-player>
<block wx:if="{{dataList.tipUrl}}"> <block wx:if="{{dataList.tipUrl}}">
<image alt="提示" mode="widthFix" src="{{dataList.tipUrl}}" style="width: 100%" /> <image alt="提示" mode="widthFix" src="{{dataList.tipUrl}}" style="width: 100%" />
</block> </block>
@@ -32,8 +49,8 @@
</view> </view>
</scroll-view> </scroll-view>
</van-tab> </van-tab>
<!-- <view class="history" bind:tap="handleViewHitory"> <view class="history" bind:tap="handleViewHitory">
<image class="icon" src="./assets/1.png" /> <image class="icon" src="./assets/1.png" />
</view> --> </view>
</van-tabs> </van-tabs>
</scroll-view> </scroll-view>
-5
View File
@@ -1,5 +0,0 @@
{
"backgroundColor": "#081120",
"disableScroll": true,
"usingComponents": {}
}
-42
View File
@@ -1,42 +0,0 @@
page {
height: 100%;
background: #081120;
}
.wxroom-redirect {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 28rpx;
color: #f4f8ff;
background:
radial-gradient(circle at 18% 12%, rgba(83, 214, 255, 0.14), transparent 28%),
linear-gradient(180deg, #0d1b31 0%, #09121f 52%, #050b15 100%);
}
.wxroom-redirect__spinner {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
border: 6rpx solid rgba(255, 255, 255, 0.1);
border-top-color: #71c1ff;
animation: wxroom-redirect-spin 0.9s linear infinite;
}
.wxroom-redirect__text {
font-size: 30rpx;
line-height: 1.4;
}
@keyframes wxroom-redirect-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
-26
View File
@@ -1,26 +0,0 @@
// pages/live/wxroom/index.ts
Page({
onLoad(options: Record<string, string | undefined>) {
const query = Object.entries(options || {})
.filter(([, value]) => value !== undefined && value !== "")
.map(
([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`,
)
.join("&");
const target = query
? `/package-live/wxroom/index?${query}`
: "/package-live/wxroom/index";
wx.redirectTo({
url: target,
fail: () => {
wx.showToast({
title: "进入直播间失败",
icon: "none",
});
},
});
},
});
-4
View File
@@ -1,4 +0,0 @@
<view class="wxroom-redirect">
<view class="wxroom-redirect__spinner"></view>
<text class="wxroom-redirect__text">正在进入直播间...</text>
</view>
+29 -18
View File
@@ -1,4 +1,5 @@
import { BASE_URL } from "../env"; import { BASE_URL, appId } from "../env";
import { base64Encode } from "./crypto";
type RequestProps = { type RequestProps = {
options: WechatMiniprogram.RequestOption; options: WechatMiniprogram.RequestOption;
loadingTitle?: string; loadingTitle?: string;
@@ -6,27 +7,23 @@ type RequestProps = {
needLogin?: boolean; needLogin?: boolean;
}; };
// 生成设备指纹字符串 // 每次请求动态生成 header,确保使用最新的 openId
const generateFingerprint = (): string => { const fingerprint = wx.getSystemInfoSync();
const info = wx.getSystemInfoSync();
return [
String(info.system),
String(info.platform),
String(info.SDKVersion),
String(info.screenWidth),
String(info.screenHeight),
].join("&");
};
export const request = <T = any>( export const request = <T = any>(
{ options, loadingTitle = "加载中", isLoading = true }: RequestProps { options, loadingTitle = "加载中", isLoading = true, needLogin = true }: RequestProps
): Promise<T> => { ): Promise<T> => {
const app = getApp<IAppOption>();
const header: any = { const header: any = {
"X-Fingerprint": generateFingerprint(), "X-Fingerprint": fingerprint,
}; };
if (needLogin) {
if (!app.checkLoginState()) {
return Promise.reject(new Error('请先登录'));
}
const openId = wx.getStorageSync("openId") || "";
header.Authorization = `Basic ${base64Encode(`${openId}:${appId}`)}`
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let showToast = false; let showToast = false;
@@ -47,13 +44,27 @@ export const request = <T = any>(
if (!res.data.code || res.data.code === 200) { if (!res.data.code || res.data.code === 200) {
resolve(res.data as T); resolve(res.data as T);
} else if (res.data.code === 401) { } else if (res.data.code === 401) {
// 保存当前页面路径,登录后跳转回来
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
if (currentPage) {
const route = `/${currentPage.route}`;
const options = currentPage.options || {};
const query = Object.entries(options)
.filter(([, value]) => value !== undefined && value !== null)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`)
.join("&");
const fullPath = query ? `${route}?${query}` : route;
wx.setStorageSync("redirectPath", fullPath);
}
wx.showModal({ wx.showModal({
title: "登录提示", title: "登录提示",
content: "请先登录", content: "请先登录",
confirmText: "去登录", confirmText: "去登录",
success: (options) => { success: (options) => {
if (options.confirm) { if (options.confirm) {
wx.navigateTo({ url: "/pages/login/login" }); // 使用 redirectTo 替换当前页,登录页无返回按钮
wx.redirectTo({ url: "/pages/login/login" });
} }
} }
}); });
+1 -1
View File
@@ -5,7 +5,7 @@
{ {
"name": "pages/live/video/video", "name": "pages/live/video/video",
"pathName": "pages/live/video/video", "pathName": "pages/live/video/video",
"query": "code=oyydS188FNmOVk89F_bm56bUwVK8", "query": "code=2KnFxIm5GPLK7SLDhvoxdHTAZFrRAM34bi",
"scene": null, "scene": null,
"launchMode": "default" "launchMode": "default"
} }