小程序审核合规:所有登录/绑号入口统一接入协议同意组件
- 新增 PrivacyAgree 通用组件(勾选 + 《用户服务协议》《隐私政策》入口) - mine 页:微信一键登录/手机号登录/微信一键绑定手机号(getPhoneNumber) 均需先同意协议 - event-detail 页:活动报名登录/短信登录/微信绑号同样强制同意 - scan-login 保持原勾选逻辑,三入口一致满足审核要求
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { View, Text } from '@tarojs/components';
|
||||
import Taro from '@tarojs/taro';
|
||||
import './PrivacyAgree.scss';
|
||||
|
||||
/**
|
||||
* 协议授权同意勾选(审核合规):未同意前禁止登录/绑定手机号等收集用户信息操作。
|
||||
* 复用组件,保证所有登录入口一致提供《用户服务协议》《隐私政策》入口。
|
||||
*/
|
||||
export default function PrivacyAgree({ agreed, onToggle, label }) {
|
||||
const open = (url) => { Taro.navigateTo({ url }); };
|
||||
return (
|
||||
<View className="pa-agree" onClick={onToggle}>
|
||||
<View className={`pa-box${agreed ? ' on' : ''}`}>{agreed ? '✓' : ''}</View>
|
||||
<Text className="pa-txt">
|
||||
{label || '我已阅读并同意'}
|
||||
<Text className="pa-link" onClick={(e) => { e.stopPropagation(); open('/pages-extra/agreement/index'); }}>《用户服务协议》</Text>
|
||||
与
|
||||
<Text className="pa-link" onClick={(e) => { e.stopPropagation(); open('/pages-extra/privacy/index'); }}>《隐私政策》</Text>
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.pa-agree { display: flex; align-items: flex-start; gap: 12rpx; margin-top: 18rpx; width: 100%; box-sizing: border-box; }
|
||||
.pa-box { width: 34rpx; height: 34rpx; border-radius: 8rpx; border: 2rpx solid #b8c0dd; display: flex; align-items: center; justify-content: center; font-size: 22rpx; color: #fff; flex-shrink: 0; background: #fff; margin-top: 2rpx; }
|
||||
.pa-box.on { background: linear-gradient(135deg, #6b8bff, #8f7bff); border-color: transparent; }
|
||||
.pa-txt { font-size: 24rpx; color: #7a86b0; line-height: 1.6; }
|
||||
.pa-link { color: #6b8bff; }
|
||||
@@ -19,6 +19,7 @@ import { isAuthed, getUser, wxLogin, wxPhoneBind, phoneLogin, bindPhone, sendCod
|
||||
import coverWide from '@/assets/huodong-wide.png';
|
||||
import Skeleton from '@/components/skeleton';
|
||||
import './index.scss';
|
||||
import PrivacyAgree from '@/components/PrivacyAgree';
|
||||
|
||||
import { PageHeader } from '@/components/TopInset';
|
||||
/* 活动报名状态(5 态) */
|
||||
@@ -70,6 +71,7 @@ export default function EventDetail() {
|
||||
/* 登录态 & 我的报名状态:登录/绑号后需重渲染,故用 state 而非渲染期一次性读取 */
|
||||
const [myBook, setMyBook] = useState(null); // {booked, auditStatus, checkinAt}
|
||||
const [authed, setAuthed] = useState(isAuthed());
|
||||
const [agree, setAgree] = useState(false); // 协议/隐私同意(登录与绑定手机号前必勾)
|
||||
const [phoneBound, setPhoneBound] = useState(() => !!(getUser() && getUser().phoneBound));
|
||||
const [showSms, setShowSms] = useState(false); // 短信兜底开关
|
||||
const [gatePhone, setGatePhone] = useState('');
|
||||
@@ -103,6 +105,7 @@ export default function EventDetail() {
|
||||
|
||||
/* 微信一键登录:wx.login + 昵称头像 → 后端建/查 wxid 账号 */
|
||||
const onWxLogin = async () => {
|
||||
if (!agree) { Taro.showToast({ title: '请先阅读并同意《用户服务协议》和《隐私政策》', icon: 'none' }); return; }
|
||||
setErr(''); setBusy(true);
|
||||
try {
|
||||
let nickName = '', avatarUrl = '';
|
||||
@@ -117,6 +120,7 @@ export default function EventDetail() {
|
||||
|
||||
/* 微信一键绑定手机号(getPhoneNumber 回调 code) */
|
||||
const onWxPhone = async (e) => {
|
||||
if (!agree) { Taro.showToast({ title: '请先阅读并同意《用户服务协议》和《隐私政策》', icon: 'none' }); return; }
|
||||
const pcode = e.detail && e.detail.code;
|
||||
if (!pcode) { setErr('已取消授权绑定'); return; }
|
||||
setErr(''); setBusy(true);
|
||||
@@ -130,6 +134,7 @@ export default function EventDetail() {
|
||||
|
||||
/* 短信兜底:未登录 → phoneLogin(登录即绑号);已登录未绑号 → bindPhone */
|
||||
const gateSmsLogin = async () => {
|
||||
if (!agree) { Taro.showToast({ title: '请先阅读并同意《用户服务协议》和《隐私政策》', icon: 'none' }); return; }
|
||||
setErr('');
|
||||
if (!/^1\d{10}$/.test(gatePhone)) { setErr('请输入正确的 11 位手机号'); return; }
|
||||
if (!gateCode) { setErr('请输入验证码'); return; }
|
||||
@@ -336,6 +341,7 @@ export default function EventDetail() {
|
||||
<Text className="ed-form-title">登录后报名</Text>
|
||||
<Text className="muted gate-tip">报名需登录并绑定手机号,用于接收活动通知。</Text>
|
||||
<Button className="btn-main" loading={busy} onClick={onWxLogin}>微信一键登录</Button>
|
||||
<PrivacyAgree agreed={agree} onToggle={() => setAgree(!agree)} />
|
||||
{!showSms && (
|
||||
<Button className="btn-ghost gate-toggle" onClick={() => setShowSms(true)}>用短信验证码登录</Button>
|
||||
)}
|
||||
@@ -360,6 +366,7 @@ export default function EventDetail() {
|
||||
<Text className="ed-form-title">绑定手机号</Text>
|
||||
<Text className="muted gate-tip">绑定后即可报名,用于接收活动通知。</Text>
|
||||
<Button className="btn-main" loading={busy} openType="getPhoneNumber" onGetPhoneNumber={onWxPhone}>微信一键绑定手机号</Button>
|
||||
<PrivacyAgree agreed={agree} onToggle={() => setAgree(!agree)} />
|
||||
{!showSms && (
|
||||
<Button className="btn-ghost gate-toggle" onClick={() => setShowSms(true)}>用短信验证码绑定</Button>
|
||||
)}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { getMyBookings, parseIso } from '@/utils/booking';
|
||||
import { API_BASE } from '@/utils/api';
|
||||
import { ensureUploadSize } from '@/utils/upload';
|
||||
import CountdownBox from '@/components/CountdownBox';
|
||||
import PrivacyAgree from '@/components/PrivacyAgree';
|
||||
import './index.scss';
|
||||
import { PageHeader } from '@/components/TopInset';
|
||||
|
||||
@@ -37,6 +38,7 @@ export default function Mine() {
|
||||
|
||||
/* 手机号登录 */
|
||||
const [showPhone, setShowPhone] = useState(false);
|
||||
const [agree, setAgree] = useState(false); // 协议/隐私同意(登录与绑定手机号前必勾)
|
||||
const [pPhone, setPPhone] = useState('');
|
||||
const [pCode, setPCode] = useState('');
|
||||
const [pCd, setPCd] = useState(0);
|
||||
@@ -69,6 +71,7 @@ export default function Mine() {
|
||||
|
||||
/* 微信一键登录 */
|
||||
const onWxLogin = async () => {
|
||||
if (!agree) { Taro.showToast({ title: '请先阅读并同意《用户服务协议》和《隐私政策》', icon: 'none' }); return; }
|
||||
setErr('');
|
||||
setBusy(true);
|
||||
try {
|
||||
@@ -121,6 +124,7 @@ export default function Mine() {
|
||||
|
||||
/* 微信官方一键绑定手机号(getPhoneNumber 回调) */
|
||||
const onWxPhone = async (e) => {
|
||||
if (!agree) { Taro.showToast({ title: '请先阅读并同意《用户服务协议》和《隐私政策》', icon: 'none' }); return; }
|
||||
const detail = (e.detail && e.detail) || {};
|
||||
const pcode = detail.code;
|
||||
if (!pcode) { Taro.showToast({ title: '已取消授权', icon: 'none' }); return; }
|
||||
@@ -150,6 +154,7 @@ export default function Mine() {
|
||||
};
|
||||
|
||||
const doPhoneLogin = async () => {
|
||||
if (!agree) { Taro.showToast({ title: '请先阅读并同意《用户服务协议》和《隐私政策》', icon: 'none' }); return; }
|
||||
if (!/^1\d{10}$/.test(pPhone)) { setErr('请输入正确的 11 位手机号'); return; }
|
||||
if (!pCode) { setErr('请输入验证码'); return; }
|
||||
setErr(''); setBusy(true);
|
||||
@@ -242,10 +247,11 @@ export default function Mine() {
|
||||
if (!user || !isAuthed()) {
|
||||
return (
|
||||
<View className="mine">
|
||||
<PageHeader no="MY" title="我的" sub="个人中心" />
|
||||
<PageHeader no="MY" title="个人中心" sub="请登录后查看个人中心详情" />
|
||||
<ScrollView className="mine-scroll" scrollY enhanced showScrollbar={false}>
|
||||
<View className="mine-card login-card">
|
||||
<Button className="btn-main wx-btn" loading={busy} onClick={onWxLogin}>微信一键登录</Button>
|
||||
<PrivacyAgree agreed={agree} onToggle={() => setAgree(!agree)} />
|
||||
<Text className="muted mine-tip">登录后可绑定手机、查看报名场次与活动签到</Text>
|
||||
<Text className="link" onClick={() => setShowPhone(!showPhone)}>{showPhone ? '收起手机号登录' : '或用手机号登录'}</Text>
|
||||
</View>
|
||||
@@ -269,7 +275,7 @@ export default function Mine() {
|
||||
{err && <Text className="err-text">{err}</Text>}
|
||||
<View className="mk-foot">
|
||||
<Text className="mk-foot-line">云南省首个由政府主办的免费大学生创业服务平台</Text>
|
||||
<Text className="mk-foot-line">云南派音人工智能科技有限公司 && 云南汇浙青创科技有限公司</Text>
|
||||
<Text className="mk-foot-line">昆明市大学生创业园 · 云超服 YunOPC-Hub</Text>
|
||||
<Text className="mk-foot-line">像素字体 BubbledotICG-FinePos · CC BY 4.0</Text>
|
||||
</View>
|
||||
</ScrollView>
|
||||
@@ -280,7 +286,7 @@ export default function Mine() {
|
||||
/* ============ 已登录:资料 + 我的报名 ============ */
|
||||
return (
|
||||
<View className="mine">
|
||||
<PageHeader no="MY" title="我的" sub="个人中心" />
|
||||
<PageHeader no="MY" title="个人中心" sub="一切都是属于您的个人数据资产" />
|
||||
<ScrollView className="mine-scroll" scrollY enhanced showScrollbar={false}>
|
||||
|
||||
{/* 资料卡 */}
|
||||
|
||||
Reference in New Issue
Block a user