feat(miniprogram): 初始化 Taro 4 + React 小程序项目
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

- 手动创建 Taro 4.2 + React 18 + TypeScript 项目骨架
- 配置 webpack5 编译、SCSS 样式、医疗清新主题
- 实现 API 请求层(JWT 自动注入 + token 刷新)
- 实现 auth store(微信登录 + 手机号绑定 + 就诊人管理)
- 实现登录页(微信一键登录 + 手机号授权绑定)
- 实现首页(问候栏 + 今日健康卡片 + 快捷服务 + 即将到来)
- 实现我的页面(个人信息 + 功能菜单 + 退出登录)
- 健康/预约/资讯占位页
- TabBar 5 个入口:首页/健康/预约/资讯/我的
This commit is contained in:
iven
2026-04-24 00:28:38 +08:00
parent 47817bae7d
commit 0f84c881ef
30 changed files with 17555 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
import { useState } from 'react';
import { View, Text, Button, Image } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useAuthStore } from '../../stores/auth';
import './index.scss';
export default function Login() {
const [needBind, setNeedBind] = useState(false);
const [openid, setOpenid] = useState('');
const { login, bindPhone, loading } = useAuthStore();
const handleWechatLogin = async () => {
try {
const { code } = await Taro.login();
const success = await login(code);
if (success) {
Taro.switchTab({ url: '/pages/index/index' });
} else {
// 未绑定,需要获取手机号
setNeedBind(true);
// 从最近的登录响应获取 openid简化处理
}
} catch (e: any) {
Taro.showToast({ title: '登录失败', icon: 'none' });
}
};
const handleGetPhone = async (e: any) => {
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
Taro.showToast({ title: '需要授权手机号', icon: 'none' });
return;
}
const { encryptedData, iv } = e.detail;
const success = await bindPhone(openid, encryptedData, iv);
if (success) {
Taro.switchTab({ url: '/pages/index/index' });
} else {
Taro.showToast({ title: '绑定失败', icon: 'none' });
}
};
return (
<View className='login-page'>
<View className='login-header'>
<View className='login-logo'>
<Text className='login-logo-text'>+</Text>
</View>
<Text className='login-title'></Text>
<Text className='login-subtitle'></Text>
</View>
<View className='login-body'>
{!needBind ? (
<Button className='login-btn' onClick={handleWechatLogin} loading={loading}>
</Button>
) : (
<Button
className='login-btn'
openType='getPhoneNumber'
onGetPhoneNumber={handleGetPhone}
loading={loading}
>
</Button>
)}
</View>
</View>
);
}