Files
YelayMaths/yelaymaths/pages/index/index.js
T
Yeomai 879e74ebda 文案配置化 + 支付开关
小程序
- 页面固定文案全部改读后台配置(购买、登录、资料、首页、分享、介绍页),代码里不再硬编码
- 已拥有课程时在当前页提示,不再跳购买页再提示
- 介绍页正文由代码常量改为读 settings.page.formula.content

服务端
- 课程相关报错文案(不存在 / 已下架 / 课时不存在 / 未购买 / 已过期 / 重复购买 / 未配置支付商品)改读配置,代码保留默认值兜底
- 新增支付总开关 pay.blocked:打开即拦截下单并下发配置里的提示;开关存在数据库里,开发库与生产库各一份(开发库已开,生产库未设)

后台
- 系统配置页按页面分组折叠,默认收起;认不出来的配置归入「其他」

配套数据(不在代码里):对应的 settings 行已写入生产库与开发库
2026-10-01 11:28:29 +08:00

184 lines
5.4 KiB
JavaScript

// pages/index/index.js
const api = require('../../utils/api');
const store = require('../../utils/store');
const config = require('../../config/index');
const app = getApp();
const updateManager = wx.getUpdateManager();
const PAGE_SIZE = 10;
/** 广播文字默认色(红),配置里没填或填得不像颜色时用它 */
const DEFAULT_BROADCAST_COLOR = '#e60012';
/**
* 只认安全的颜色写法,其余一律回退默认色。
* 后台是文本框,填错一个字符不该把首页样式搞坏,也不该让这个值变成注入点。
*/
function safeColor(value) {
const v = String(value || '').trim();
if (/^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$/.test(v)) return v;
if (/^rgba?\([\d\s.,%]+\)$/.test(v)) return v;
if (/^hsla?\([\d\s.,%]+\)$/.test(v)) return v;
if (/^[a-zA-Z]{3,20}$/.test(v)) return v; // red / orange / tomato ...
return DEFAULT_BROADCAST_COLOR;
}
Page({
data: {
staticBase: config.STATIC_BASE_URL,
banners: [],
courseList: [],
freeCourseList: [],
articles: [],
page: 1,
articleTotal: 0,
loadingMore: false,
noMore: false,
title1: '',
title2: '',
title3: '',
footerText: '',
/** 首页信息广播文案,空则不显示整条 */
broadcast: '',
/** 文案超出宽度时才横向滚动 */
broadcastScroll: false,
/** 广播文字颜色,取自 home.broadcast_color */
broadcastColor: DEFAULT_BROADCAST_COLOR,
},
onLoad() {
// 等应用初始化完成再取配置,避免首屏读到空缓存
app.ready().then(() => this.applyConfig());
this.loadAll();
},
applyConfig() {
this.setData(
{
title1: store.cfg('home.title.paid'),
title2: store.cfg('home.title.trial'),
title3: store.cfg('home.title.public'),
footerText: store.cfg('home.footer_text'),
broadcast: store.cfg('home.broadcast') || '',
broadcastColor: safeColor(store.cfg('home.broadcast_color')),
broadcastScroll: false,
},
// 等渲染完再量宽度:只有真放不下才让它滚动,短文案就静静居中
() => this.measureBroadcast(),
);
},
/** 量一下广播文字有没有超出容器,超了才加滚动动画。量不到就当不超。 */
measureBroadcast() {
if (!this.data.broadcast) return;
const query = wx.createSelectorQuery().in(this);
query.select('.broadcast-body').boundingClientRect();
query.select('.broadcast-text').boundingClientRect();
query.exec((res) => {
const box = res && res[0];
const text = res && res[1];
if (!box || !text) return;
if (text.width > box.width) this.setData({ broadcastScroll: true });
});
},
loadAll() {
this.setData({ page: 1, noMore: false });
api
.getBanners()
.then((list) => this.setData({ banners: list || [] }))
.catch((err) => console.error('[index] 轮播加载失败', err));
api
.getHomeCourses()
.then((list) => this.setData({ courseList: list || [] }))
.catch((err) => console.error('[index] 新课加载失败', err));
api
.getHomeFreeCourses()
.then((list) => this.setData({ freeCourseList: list || [] }))
.catch((err) => console.error('[index] 试听列表加载失败', err));
api
.getArticles(1, PAGE_SIZE)
.then((res) => this.setData({ articles: res.items || [], articleTotal: res.total || 0, page: 1 }))
.catch((err) => console.error('[index] 文章加载失败', err));
},
onShow() {
updateManager.onCheckForUpdate(() => {});
updateManager.onUpdateReady(() => {
wx.showModal({
title: store.cfg('app.update.title'),
showCancel: false,
content: store.cfg('app.update.content'),
success: (res) => {
if (res.confirm) updateManager.applyUpdate();
},
});
});
updateManager.onUpdateFailed(() => {});
},
onPullDownRefresh() {
Promise.all([this.reload()]).then(() => wx.stopPullDownRefresh());
},
async reload() {
try {
const cfg = await api.getConfig();
store.setSiteConfig(cfg);
this.applyConfig();
} catch (err) {
console.warn('[index] 配置刷新失败', err);
}
this.loadAll();
},
onReachBottom() {
if (this.data.loadingMore || this.data.noMore) return;
const next = this.data.page + 1;
this.setData({ loadingMore: true });
api
.getArticles(next, PAGE_SIZE)
.then((res) => {
const merged = this.data.articles.concat(res.items || []);
this.setData({
articles: merged,
page: next,
loadingMore: false,
noMore: merged.length >= (res.total || 0),
});
})
.catch((err) => {
console.error('[index] 加载更多失败', err);
this.setData({ loadingMore: false });
});
},
toArticle(e) {
wx.navigateTo({ url: '/pages/WebPageDetails/WebPageDetails?id=' + e.currentTarget.dataset.id });
},
toNewCourse(e) {
wx.navigateTo({ url: '/pages/IndexNewCourseDetails/IndexNewCourseDetails?id=' + e.currentTarget.dataset.id });
},
toFreeCourse(e) {
wx.navigateTo({
url: '/pages/IndexFreeCourseDetails/IndexFreeCourseDetails?seriesNo=' + e.currentTarget.dataset.series,
});
},
onShareAppMessage() {
return { title: store.cfg('share.title'), path: '/pages/index/index' };
},
onShareTimeline() {
return { title: store.cfg('share.title') };
},
});