feat(home): 实现游客模式与登录流程优化

- 添加游客模式,展示公共内容无需强制登录
- 实现游客模块展示打卡、计划、关于我们等功能介绍
- 添加产品介绍页面显示学习成长工具详情
- 优化登录流程,添加用户协议和隐私政策同意选项
- 更新角色标签将老师改为管理员,学员改为成员
- 添加注销账号功能替代退出登录
- 实现webview页面域名白名单安全验证
- 添加隐私政策和用户协议页面
- 优化页面跳转逻辑和错误提示信息
- 统一项目名称从明缘进阶场改为明缘云道场
This commit is contained in:
王宏豪
2026-09-03 18:57:47 +08:00
parent d7fc8c8982
commit 1dd4fa3f41
20 changed files with 588 additions and 57 deletions

View File

@@ -16,6 +16,7 @@ Page({
displayName: '',
encouragement: '',
modules: [],
introData: null,
loading: true,
error: '',
skeletonItems: [0, 1, 2, 3, 4, 5, 6, 7, 8]
@@ -27,12 +28,32 @@ Page({
if (!silent) {
this.setData({ loading: true, error: '' });
}
const token = auth.getToken();
if (!token) {
// 游客模式:展示公开内容,不强制登录
this.setData({
loading: false,
error: '',
greeting: '欢迎来到明缘云道场',
displayName: '游客',
encouragement: '登录后即可打卡、提交计划',
modules: this.getGuestModules(),
introData: this.getIntroData()
});
return Promise.resolve();
}
return auth.ensureLogin()
.then((token) => {
if (!token) {
// 新用户未绑定手机号,引导去「我的」完成绑定,避免无 token 请求受保护接口导致 401
this.setData({ loading: false, error: '请先完成微信绑定后再进入' });
setTimeout(() => wx.switchTab({ url: '/pages/profile/profile' }), 500);
this.setData({
loading: false,
error: '',
greeting: '欢迎来到明缘云道场',
displayName: '游客',
encouragement: '登录后即可打卡、提交计划',
modules: this.getGuestModules(),
introData: this.getIntroData()
});
return;
}
return api.request({ url: '/api/v1/pages/home' });
@@ -45,7 +66,8 @@ Page({
greeting: res.greeting || '',
displayName: res.display_name || '',
encouragement: res.encouragement || '',
modules: (res.modules || []).filter((m) => m.visible)
modules: (res.modules || []).filter((m) => m.visible),
introData: null
});
})
.catch((err) => {
@@ -53,6 +75,42 @@ Page({
});
},
getGuestModules() {
return [
{ id: 'checkin', icon: '📋', label: '打卡介绍', desc: '每日打卡记录', visible: true, url: '', guest: true },
{ id: 'plan', icon: '📝', label: '计划介绍', desc: '周/月/年计划', visible: true, url: '', guest: true },
{ id: 'about', icon: 'ℹ️', label: '关于我们', desc: '了解明缘云道场', visible: true, url: '', guest: true }
];
},
getIntroData() {
return {
title: '产品介绍',
sections: [
{
icon: '📚',
heading: '学习成长工具',
text: '明缘云道场是一款专注于个人成长的效率工具,通过每周打卡与计划管理,帮助你养成良好的学习习惯,实现持续进步。好好学习,天天向上。'
},
{
icon: '✅',
heading: '周打卡',
text: '每周打卡,支持多种状态记录(正常、迟到、请假、早退、不参加讨论等)。查看小组打卡情况,与同学们互相监督、共同进步。'
},
{
icon: '📝',
heading: '计划管理',
text: '提供周计划、月计划、年计划三级管理,支持文字与图片上传。清晰追踪每个周期的目标完成情况,让成长有迹可循。'
},
{
icon: '👥',
heading: '小组共学',
text: '加入学习小组,查看组员的打卡与计划完成情况。在集体的氛围中保持学习动力,与志同道合的伙伴一起成长。'
}
]
};
},
onPullDownRefresh() {
this.loadHome(true).then(() => {
wx.stopPullDownRefresh();
@@ -63,12 +121,37 @@ Page({
this.loadHome();
},
goLogin() {
wx.switchTab({ url: '/pages/profile/profile' });
},
onModuleTap(e) {
const url = e.currentTarget.dataset.url;
if (!url) return;
const token = auth.getToken();
const sep = url.indexOf('?') >= 0 ? '&' : '?';
const target = url + sep + 'token=' + encodeURIComponent(token);
wx.navigateTo({ url: '/pages/webview/webview?url=' + encodeURIComponent(target) });
// 游客模式:引导登录
if (!token) {
wx.showModal({
title: '登录提示',
content: '登录后使用此功能,是否前往登录?',
confirmText: '去登录',
confirmColor: '#C67B30',
success: (res) => {
if (res.confirm) {
wx.switchTab({ url: '/pages/profile/profile' });
}
}
});
return;
}
if (!url) return;
// 通过 webview redirect 接口:后端验证 token 后设置 HttpOnly cookie,再 302 到目标页
// H5 页面内的同域请求会自动携带 cookie,H5 端零改动
const redirectUrl = api.BASE_URL + '/api/v1/webview/redirect'
+ '?token=' + encodeURIComponent(token)
+ '&target=' + encodeURIComponent(url);
wx.navigateTo({ url: '/pages/webview/webview?url=' + encodeURIComponent(redirectUrl) });
}
});

View File

@@ -15,6 +15,9 @@
<view class="hero-chip" wx:if="{{greeting}}">{{greeting}}</view>
<view class="hero-name">{{displayName || '欢迎回来'}}</view>
<view class="hero-enc" wx:if="{{encouragement}}">{{encouragement}}</view>
<view class="hero-login-tip" wx:if="{{!displayName || displayName === '游客'}}" hover-class="btn-hover" bindtap="goLogin">
登录账号解锁完整功能 ›
</view>
</view>
<view class="error-bar" wx:if="{{error}}">
@@ -37,6 +40,18 @@
</view>
</view>
<!-- 游客模式:明缘介绍 -->
<view class="intro-section" wx:if="{{introData && introData.sections}}">
<view class="intro-title">{{introData.title}}</view>
<view class="intro-card" wx:for="{{introData.sections}}" wx:key="heading">
<view class="intro-icon">{{item.icon}}</view>
<view class="intro-body">
<view class="intro-heading">{{item.heading}}</view>
<view class="intro-text">{{item.text}}</view>
</view>
</view>
</view>
<view class="empty" wx:if="{{modules.length === 0 && !error}}">暂无功能入口</view>
</block>
</view>
</view>

View File

@@ -153,4 +153,56 @@
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.hero-login-tip {
margin-top: 24rpx;
display: inline-block;
padding: 12rpx 28rpx;
background: rgba(198, 123, 48, 0.25);
border: 1rpx solid rgba(227, 179, 124, 0.4);
border-radius: 999rpx;
color: #E3B37C;
font-size: 24rpx;
}
/* ---- intro section ---- */
.intro-section {
margin-top: 40rpx;
}
.intro-title {
font-size: 32rpx;
font-weight: 700;
color: #2C1810;
margin-bottom: 20rpx;
padding-left: 4rpx;
}
.intro-card {
display: flex;
align-items: flex-start;
background: #fff;
border-radius: 16rpx;
padding: 28rpx 24rpx;
margin-bottom: 16rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03);
}
.intro-icon {
font-size: 40rpx;
margin-right: 20rpx;
flex-shrink: 0;
line-height: 1;
padding-top: 2rpx;
}
.intro-body {
flex: 1;
min-width: 0;
}
.intro-heading {
font-size: 28rpx;
font-weight: 700;
color: #C67B30;
margin-bottom: 10rpx;
}
.intro-text {
font-size: 25rpx;
color: #5A4A3A;
line-height: 1.8;
}