feat(auth,mp): 患者登录流程优化 — 智能合并 + 角色冻结 + 页面冻结
- 智能合并:微信注册时用手机号盲索引匹配已有患者档案,避免重复建 档(AuthState 添加 PiiCrypto + ensure_patient_record 增加盲索引查询) - 角色冻结:小程序仅允许患者角色登录,医护角色被拦截 (auth_service.rs 添加反向拦截 + 登录页移除 credential login 表单) - 页面冻结:10 个非核心页面替换为 FrozenPage 占位组件(用药/知情同意 /透析/家属/诊断/事件),移除 profile 导航入口,移除医生端预加载 - 医生端代码保留,仅隐藏入口,后续可零成本恢复 讨论记录:docs/discussions/2026-05-23-account-registration-login-flow.md
This commit is contained in:
@@ -1,131 +1,5 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { View, Text } from '@tarojs/components';
|
||||
import Taro, { useReachBottom } from '@tarojs/taro';
|
||||
import { usePageData } from '@/hooks/usePageData';
|
||||
import { getCachedPatientId } from '@/services/request';
|
||||
import { listConsents, revokeConsent } from '@/services/consent';
|
||||
import type { Consent } from '@/services/consent';
|
||||
import EmptyState from '@/components/EmptyState';
|
||||
import Loading from '@/components/Loading';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import PageShell from '@/components/ui/PageShell';
|
||||
import './index.scss';
|
||||
import FrozenPage from '@/components/FrozenPage';
|
||||
|
||||
const CONSENT_TYPE_MAP: Record<string, string> = {
|
||||
data_processing: '数据处理同意',
|
||||
health_data_collection: '健康数据采集',
|
||||
research_use: '科研使用',
|
||||
third_party_share: '第三方共享',
|
||||
genetic_testing: '基因检测',
|
||||
telemedicine: '远程医疗',
|
||||
};
|
||||
|
||||
const STATUS_MAP: Record<string, { label: string; cls: string }> = {
|
||||
granted: { label: '已签署', cls: 'granted' },
|
||||
revoked: { label: '已撤回', cls: 'revoked' },
|
||||
};
|
||||
|
||||
export default function ConsentList() {
|
||||
const modeClass = useElderClass();
|
||||
const [consents, setConsents] = useState<Consent[]>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [revoking, setRevoking] = useState<string | null>(null);
|
||||
const [hasPatient, setHasPatient] = useState(true);
|
||||
|
||||
const fetchData = useCallback(async (p: number, append = false) => {
|
||||
const patientId = getCachedPatientId();
|
||||
if (!patientId) {
|
||||
setConsents([]);
|
||||
setHasPatient(false);
|
||||
return;
|
||||
}
|
||||
setHasPatient(true);
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await listConsents(patientId, { page: p, page_size: 20 });
|
||||
const list = res.data || [];
|
||||
setConsents(append ? (prev) => [...prev, ...list] : list);
|
||||
setTotal(res.total);
|
||||
setPage(p);
|
||||
} catch (err) {
|
||||
console.warn('[consent] 加载失败:', err);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
usePageData(async () => { await fetchData(1); }, { throttleMs: 10000, enablePullDown: true });
|
||||
|
||||
useReachBottom(() => {
|
||||
if (!loading && consents.length < total) {
|
||||
fetchData(page + 1, true);
|
||||
}
|
||||
});
|
||||
|
||||
const handleRevoke = async (consent: Consent) => {
|
||||
const { confirm } = await Taro.showModal({
|
||||
title: '确认撤回',
|
||||
content: `确定要撤回「${CONSENT_TYPE_MAP[consent.consent_type] || consent.consent_type}」的同意吗?`,
|
||||
});
|
||||
if (!confirm) return;
|
||||
setRevoking(consent.id);
|
||||
try {
|
||||
const updated = await revokeConsent(consent.id, consent.version);
|
||||
setConsents((prev) => prev.map((c) => c.id === updated.id ? updated : c));
|
||||
Taro.showToast({ title: '已撤回', icon: 'success' });
|
||||
} catch (err) {
|
||||
console.warn('[consent] 撤回失败:', err);
|
||||
Taro.showToast({ title: '撤回失败', icon: 'none' });
|
||||
} finally {
|
||||
setRevoking(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PageShell className={modeClass}>
|
||||
<Text className='page-title'>知情同意</Text>
|
||||
|
||||
<View className='consent-list'>
|
||||
{consents.map((c) => {
|
||||
const si = STATUS_MAP[c.status] || { label: c.status, cls: '' };
|
||||
const typeName = CONSENT_TYPE_MAP[c.consent_type] || c.consent_type;
|
||||
return (
|
||||
<View className='consent-card' key={c.id}>
|
||||
<View className='consent-card__header'>
|
||||
<Text className='consent-card__type'>{typeName}</Text>
|
||||
<Text className={`status-tag ${si.cls}`}>{si.label}</Text>
|
||||
</View>
|
||||
<Text className='consent-card__scope'>范围: {c.consent_scope}</Text>
|
||||
{c.granted_at && (
|
||||
<Text className='consent-card__date'>签署时间: {c.granted_at}</Text>
|
||||
)}
|
||||
{c.revoked_at && (
|
||||
<Text className='consent-card__date'>撤回时间: {c.revoked_at}</Text>
|
||||
)}
|
||||
{c.expiry_date && (
|
||||
<Text className='consent-card__expiry'>有效期至: {c.expiry_date}</Text>
|
||||
)}
|
||||
{c.status === 'granted' && (
|
||||
<View
|
||||
className={`revoke-btn ${revoking === c.id ? 'revoke-btn--disabled' : ''}`}
|
||||
onClick={() => handleRevoke(c)}
|
||||
>
|
||||
<Text className='revoke-btn__text'>{revoking === c.id ? '处理中...' : '撤回同意'}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
|
||||
{consents.length === 0 && !loading && (
|
||||
<EmptyState text={hasPatient ? '暂无知情同意记录' : '请先在就诊人管理中选择就诊人'} />
|
||||
)}
|
||||
|
||||
{loading && <Loading />}
|
||||
</PageShell>
|
||||
);
|
||||
export default function ConsentsPage() {
|
||||
return <FrozenPage />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user