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,101 @@
import { create } from 'zustand';
import Taro from '@tarojs/taro';
import * as authApi from '../services/auth';
interface AuthState {
token: string | null;
refreshToken: string | null;
user: { id: string; username: string; display_name?: string; phone?: string } | null;
currentPatient: authApi.PatientInfo | null;
patients: authApi.PatientInfo[];
loading: boolean;
login: (code: string) => Promise<boolean>;
bindPhone: (openid: string, encryptedData: string, iv: string) => Promise<boolean>;
setCurrentPatient: (patient: authApi.PatientInfo) => void;
loadPatients: () => Promise<void>;
logout: () => void;
restore: () => void;
}
export const useAuthStore = create<AuthState>((set, get) => ({
token: null,
refreshToken: null,
user: null,
currentPatient: null,
patients: [],
loading: false,
restore: () => {
const token = Taro.getStorageSync('access_token') || null;
const refreshToken = Taro.getStorageSync('refresh_token') || null;
const user = Taro.getStorageSync('user') || null;
const currentPatient = Taro.getStorageSync('current_patient') || null;
set({ token, refreshToken, user, currentPatient });
},
login: async (code: string) => {
set({ loading: true });
try {
const resp = await authApi.wechatLogin(code);
if (resp.bound && resp.token) {
const { access_token, refresh_token, user } = resp.token;
Taro.setStorageSync('access_token', access_token);
Taro.setStorageSync('refresh_token', refresh_token);
Taro.setStorageSync('user', user);
Taro.setStorageSync('tenant_id', user.tenant_id || '');
set({ token: access_token, refreshToken: refresh_token, user, loading: false });
return true;
}
set({ loading: false });
return false;
} catch {
set({ loading: false });
return false;
}
},
bindPhone: async (openid: string, encryptedData: string, iv: string) => {
set({ loading: true });
try {
const resp: any = await authApi.wechatBindPhone(openid, encryptedData, iv);
const { access_token, refresh_token, user } = resp;
Taro.setStorageSync('access_token', access_token);
Taro.setStorageSync('refresh_token', refresh_token);
Taro.setStorageSync('user', user);
set({ token: access_token, refreshToken: refresh_token, user, loading: false });
return true;
} catch {
set({ loading: false });
return false;
}
},
setCurrentPatient: (patient) => {
Taro.setStorageSync('current_patient_id', patient.id);
Taro.setStorageSync('current_patient', patient);
set({ currentPatient: patient });
},
loadPatients: async () => {
try {
const patients = await authApi.getPatients();
set({ patients });
if (patients.length > 0 && !get().currentPatient) {
get().setCurrentPatient(patients[0]);
}
} catch {
// ignore
}
},
logout: () => {
Taro.removeStorageSync('access_token');
Taro.removeStorageSync('refresh_token');
Taro.removeStorageSync('user');
Taro.removeStorageSync('current_patient');
Taro.removeStorageSync('current_patient_id');
set({ token: null, refreshToken: null, user: null, currentPatient: null, patients: [] });
Taro.redirectTo({ url: '/pages/login/index' });
},
}));