157 lines
4.6 KiB
JavaScript
157 lines
4.6 KiB
JavaScript
// pages/VideoDetails/VideoDetails.js
|
|
const api = require('../../utils/api');
|
|
const store = require('../../utils/store');
|
|
const format = require('../../utils/format');
|
|
|
|
const POLL_INTERVAL = 1500;
|
|
const POLL_MAX = 8;
|
|
|
|
Page({
|
|
data: {
|
|
code: '',
|
|
owned: false,
|
|
loading: true,
|
|
buying: false,
|
|
VideoTitle: '',
|
|
VideoDetails: '',
|
|
coverUrl: '',
|
|
priceYuan: '',
|
|
playdata: [],
|
|
VideoPlayerSrc: '',
|
|
HiddenVideoControl: true,
|
|
top: 0,
|
|
},
|
|
|
|
onLoad(options) {
|
|
const code = options.code;
|
|
if (!code) {
|
|
wx.switchTab({ url: '/pages/kecheng/kecheng' });
|
|
return;
|
|
}
|
|
this.setData({ code: code });
|
|
this.load(code);
|
|
},
|
|
|
|
onShow() {
|
|
if (this.data.code && this.data.owned) this.load(this.data.code);
|
|
},
|
|
|
|
load(code) {
|
|
api
|
|
.getCourse(code)
|
|
.then((course) => {
|
|
this.setData({
|
|
loading: false,
|
|
owned: course.owned,
|
|
VideoTitle: course.title || '',
|
|
VideoDetails: course.intro || '',
|
|
coverUrl: course.coverUrl || '',
|
|
priceYuan: format.fenToYuan(course.priceFen),
|
|
});
|
|
if (course.owned) this.loadLessons(code);
|
|
})
|
|
.catch((err) => {
|
|
this.setData({ loading: false });
|
|
wx.showToast({ title: err.message || '课程加载失败', icon: 'none' });
|
|
setTimeout(() => wx.switchTab({ url: '/pages/kecheng/kecheng' }), 1500);
|
|
});
|
|
},
|
|
|
|
loadLessons(code) {
|
|
api
|
|
.getLessons(code)
|
|
.then((list) => {
|
|
const rows = (list || []).map((item) => ({
|
|
chapterId: item.chapterId,
|
|
playId: item.playId,
|
|
chapterTitle: item.chapterTitle,
|
|
lessonTitle: item.lessonTitle,
|
|
duration: item.duration,
|
|
publishedAt: format.toDateString(item.publishedAt),
|
|
coverUrl: item.coverUrl,
|
|
}));
|
|
this.setData({ playdata: rows });
|
|
})
|
|
.catch((err) => {
|
|
this.setData({ playdata: [] });
|
|
console.warn('[VideoDetails] 课时加载失败', err);
|
|
});
|
|
},
|
|
|
|
/** 点课时:向后端换一次性播放地址,视频本身不直接暴露。 */
|
|
onTapLesson(e) {
|
|
const item = this.data.playdata[e.currentTarget.dataset.index];
|
|
if (!item) return;
|
|
|
|
wx.showLoading({ title: '加载中', mask: true });
|
|
api
|
|
.getPlayUrl(this.data.code, item.chapterId, item.playId)
|
|
.then((res) => {
|
|
wx.hideLoading();
|
|
this.setData({ VideoPlayerSrc: res.url, HiddenVideoControl: false, top: 420 });
|
|
})
|
|
.catch((err) => {
|
|
wx.hideLoading();
|
|
wx.showToast({ title: err.message || '无法播放', icon: 'none' });
|
|
});
|
|
},
|
|
|
|
/** 购买:下单 → 拉起支付 → 轮询订单状态确认发货。 */
|
|
buyNow() {
|
|
if (this.data.buying) return;
|
|
if (typeof wx.requestVirtualPayment !== 'function') {
|
|
wx.showToast({ title: '当前微信版本不支持支付,请升级微信', icon: 'none' });
|
|
return;
|
|
}
|
|
|
|
this.setData({ buying: true });
|
|
wx.showLoading({ title: '发起支付', mask: true });
|
|
|
|
api
|
|
.createOrder(this.data.code)
|
|
.then((order) => {
|
|
wx.hideLoading();
|
|
return api.requestVirtualPayment(order.payData).then(() => order.outTradeNo);
|
|
})
|
|
.then((outTradeNo) => this.waitForDelivery(outTradeNo))
|
|
.catch((err) => {
|
|
wx.hideLoading();
|
|
this.setData({ buying: false });
|
|
// 用户主动取消不算错误
|
|
if (err && err.errMsg && err.errMsg.indexOf('cancel') >= 0) return;
|
|
wx.showToast({ title: (err && err.message) || '支付失败', icon: 'none' });
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 支付成功后权益由服务端发货推送发放,存在秒级延迟,这里轮询确认。
|
|
* 即便轮询全部超时也不会丢单:服务端有定时查单兜底。
|
|
*/
|
|
waitForDelivery(outTradeNo) {
|
|
let attempts = 0;
|
|
const poll = () => {
|
|
attempts += 1;
|
|
return api.getOrder(outTradeNo).then((order) => {
|
|
if (order.status === 2) {
|
|
this.setData({ buying: false, owned: true });
|
|
wx.showToast({ title: '购买成功', icon: 'success', mask: true });
|
|
this.load(this.data.code);
|
|
return;
|
|
}
|
|
if (attempts >= POLL_MAX || order.status === 3 || order.status === 4) {
|
|
this.setData({ buying: false });
|
|
wx.showToast({ title: '支付已提交,开通可能稍有延迟', icon: 'none', duration: 3000 });
|
|
return;
|
|
}
|
|
return new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL)).then(poll);
|
|
});
|
|
};
|
|
return poll();
|
|
},
|
|
|
|
onShareAppMessage() {
|
|
return { title: '叶雷讲数学', path: '/pages/index/index' };
|
|
},
|
|
});
|
|
|