feat(mp): 登录页 UX 优化 — 协议区域就近显示

- 协议勾选移至对应操作区域(微信登录 + 绑定区)更直觉
- 统一 SHOW_DEV_LOGIN 常量控制开发模式入口
- 抽取 requireAgreement() 复用协议检查逻辑
This commit is contained in:
iven
2026-05-25 13:44:35 +08:00
parent 185f411495
commit 485b9bb926

View File

@@ -9,6 +9,7 @@ declare const __wxConfig: Record<string, unknown> | undefined;
const IS_DEV = process.env.NODE_ENV !== 'production'; const IS_DEV = process.env.NODE_ENV !== 'production';
const IS_SIMULATOR = typeof __wxConfig !== 'undefined' && (__wxConfig as Record<string, unknown>)?.envVersion === 'develop'; const IS_SIMULATOR = typeof __wxConfig !== 'undefined' && (__wxConfig as Record<string, unknown>)?.envVersion === 'develop';
const SHOW_DEV_LOGIN = (IS_DEV || IS_SIMULATOR) && !!(process.env.TARO_APP_DEV_USER && process.env.TARO_APP_DEV_PASS);
export default function Login() { export default function Login() {
const [agreed, setAgreed] = useState(false); const [agreed, setAgreed] = useState(false);
@@ -40,11 +41,16 @@ export default function Login() {
} }
}; };
const handleWechatLogin = async () => { const requireAgreement = () => {
if (!agreed) { if (!agreed) {
Taro.showToast({ title: '请先阅读并同意用户协议', icon: 'none' }); Taro.showToast({ title: '请先阅读并同意用户协议', icon: 'none' });
return; return false;
} }
return true;
};
const handleWechatLogin = async () => {
if (!requireAgreement()) return;
try { try {
const { code } = await Taro.login(); const { code } = await Taro.login();
const result = await login(code); const result = await login(code);
@@ -60,23 +66,6 @@ export default function Login() {
} }
}; };
const handleDevQuickLogin = async () => {
const devUser = process.env.TARO_APP_DEV_USER || '';
const devPass = process.env.TARO_APP_DEV_PASS || '';
if (!devUser || !devPass) {
Taro.showToast({ title: '未配置开发账号', icon: 'none' });
return;
}
try {
const success = await credentialLogin(devUser, devPass);
if (success) {
navigateAfterLogin();
}
} catch (err: unknown) {
Taro.showToast({ title: err instanceof Error ? err.message : '登录失败', icon: 'none' });
}
};
const handleGetPhone = async (e: { detail: { errMsg: string; encryptedData: string; iv: string } }) => { const handleGetPhone = async (e: { detail: { errMsg: string; encryptedData: string; iv: string } }) => {
if (e.detail.errMsg !== 'getPhoneNumber:ok') { if (e.detail.errMsg !== 'getPhoneNumber:ok') {
Taro.showToast({ title: '需要授权手机号', icon: 'none' }); Taro.showToast({ title: '需要授权手机号', icon: 'none' });
@@ -100,6 +89,7 @@ export default function Login() {
}; };
// DevTools 中 getPhoneNumber 不可用,直接传 mock 数据绕过微信 SDK // DevTools 中 getPhoneNumber 不可用,直接传 mock 数据绕过微信 SDK
// 仅在后端 wechat_dev_mode=true 时有效,后端会生成 mock 手机号
const handleDevBindPhone = async () => { const handleDevBindPhone = async () => {
try { try {
const success = await bindPhone('dev_mock', 'dev_mock'); const success = await bindPhone('dev_mock', 'dev_mock');
@@ -117,6 +107,21 @@ export default function Login() {
} }
}; };
const handleDevQuickLogin = async () => {
if (!requireAgreement()) return;
const devUser = process.env.TARO_APP_DEV_USER || '';
const devPass = process.env.TARO_APP_DEV_PASS || '';
if (!devUser || !devPass) return;
try {
const success = await credentialLogin(devUser, devPass);
if (success) {
navigateAfterLogin();
}
} catch (err: unknown) {
Taro.showToast({ title: err instanceof Error ? err.message : '登录失败', icon: 'none' });
}
};
return ( return (
<View className="login-page"> <View className="login-page">
{/* 品牌区 */} {/* 品牌区 */}
@@ -130,16 +135,32 @@ export default function Login() {
{!needBind ? ( {!needBind ? (
<> <>
{/* 微信一键登录 */} {/* 微信一键登录(主按钮) */}
<View className="login-wechat-btn" onClick={handleWechatLogin}> <View className="login-wechat-btn" onClick={handleWechatLogin}>
<Text className="login-wechat-icon"></Text> <Text className="login-wechat-icon"></Text>
<Text className="login-wechat-text"></Text> <Text className="login-wechat-text"></Text>
</View> </View>
{/* 协议 */}
<View className="agreement-row">
<View
className={`agreement-check ${agreed ? 'checked' : ''}`}
onClick={() => setAgreed(!agreed)}
>
{agreed && <Text className="agreement-check-mark">&#10003;</Text>}
</View>
<Text className="agreement-text">
<Text className="agreement-link" onClick={() => safeNavigateTo('/pages/legal/user-agreement')}></Text>
<Text className="agreement-link" onClick={() => safeNavigateTo('/pages/legal/privacy-policy')}></Text>
</Text>
</View>
</> </>
) : ( ) : (
<View className="login-bind-section"> <View className="login-bind-section">
{/* 真机:微信手机号授权 */} {/* 真机:微信手机号授权 */}
{!(IS_DEV || IS_SIMULATOR) && ( {!SHOW_DEV_LOGIN && (
<Button <Button
className="login-btn-bind" className="login-btn-bind"
openType="getPhoneNumber" openType="getPhoneNumber"
@@ -151,7 +172,7 @@ export default function Login() {
</Button> </Button>
)} )}
{/* DevTools跳过微信 SDK 直接调后端(后端 wechat_dev_mode 会用 mock 手机号) */} {/* DevTools跳过微信 SDK 直接调后端(后端 wechat_dev_mode 会用 mock 手机号) */}
{(IS_DEV || IS_SIMULATOR) && ( {SHOW_DEV_LOGIN && (
<Button <Button
className="login-btn-bind" className="login-btn-bind"
onClick={handleDevBindPhone} onClick={handleDevBindPhone}
@@ -161,29 +182,28 @@ export default function Login() {
</Button> </Button>
)} )}
{/* 协议 */}
<View className="agreement-row" style={{ marginTop: '16px' }}>
<View
className={`agreement-check ${agreed ? 'checked' : ''}`}
onClick={() => setAgreed(!agreed)}
>
{agreed && <Text className="agreement-check-mark">&#10003;</Text>}
</View>
<Text className="agreement-text">
<Text className="agreement-link" onClick={() => safeNavigateTo('/pages/legal/user-agreement')}></Text>
<Text className="agreement-link" onClick={() => safeNavigateTo('/pages/legal/privacy-policy')}></Text>
</Text>
</View>
</View> </View>
)} )}
{/* 协议 */}
<View className="agreement-row">
<View
className={`agreement-check ${agreed ? 'checked' : ''}`}
onClick={() => setAgreed(!agreed)}
>
{agreed && <Text className="agreement-check-mark">&#10003;</Text>}
</View>
<Text className="agreement-text">
<Text className="agreement-link" onClick={() => safeNavigateTo('/pages/legal/user-agreement')}></Text>
<Text className="agreement-link" onClick={() => safeNavigateTo('/pages/legal/privacy-policy')}></Text>
</Text>
</View>
<View style={{ flex: 1 }} /> <View style={{ flex: 1 }} />
{/* 开发模式 */} {/* 开发模式快速登录 — 仅 dev 构建 + DevTools 中显示 */}
{(IS_DEV || IS_SIMULATOR) && ( {SHOW_DEV_LOGIN && (
<View className="login-dev-btn" onClick={handleDevQuickLogin}> <View className="login-dev-btn" onClick={handleDevQuickLogin}>
<Text className="login-dev-btn-text"> </Text> <Text className="login-dev-btn-text"> </Text>
</View> </View>