refactor(mp): 迁移患者咨询列表页 — 使用统一组件库
- PageShell 替代手写 min-height/bg/padding - ContentCard 替代手写 session-card 卡片样式 - StatusTag 替代手写 session-tag 状态标签 - LoadingCard 替代初始加载态 - EmptyState 替代手写空状态 - ErrorState 替代手写错误状态 - 精简 SCSS 删除已接管样式,保留按钮/头像/角标等页面特有样式 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,14 @@
|
|||||||
@import '../../styles/variables.scss';
|
@import '../../styles/variables.scss';
|
||||||
@import '../../styles/mixins.scss';
|
@import '../../styles/mixins.scss';
|
||||||
|
|
||||||
.consultation-page {
|
// PageShell 已接管:min-height, background, padding
|
||||||
min-height: 100vh;
|
// ContentCard 已接管:session-card 背景/圆角/阴影/触摸反馈
|
||||||
background: $bg;
|
// StatusTag 已接管:session-tag 标签样式
|
||||||
}
|
// LoadingCard 已接管:初始加载骨架屏
|
||||||
|
// EmptyState 已接管:空状态样式
|
||||||
|
// ErrorState 已接管:错误状态样式
|
||||||
|
|
||||||
.consultation-body {
|
/* ─── 副标题(页面特有) ─── */
|
||||||
padding: 12px 24px 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── 副标题 ─── */
|
|
||||||
.consultation-subtitle {
|
.consultation-subtitle {
|
||||||
font-size: var(--tk-font-cap);
|
font-size: var(--tk-font-cap);
|
||||||
color: var(--tk-text-secondary);
|
color: var(--tk-text-secondary);
|
||||||
@@ -18,7 +16,7 @@
|
|||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── 发起咨询按钮 — 实心主色 ─── */
|
/* ─── 发起咨询按钮(页面特有) ─── */
|
||||||
.consultation-create-btn {
|
.consultation-create-btn {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border-radius: $r;
|
border-radius: $r;
|
||||||
@@ -38,80 +36,23 @@
|
|||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── 居中容器 ─── */
|
/* ─── 会话列表布局 ─── */
|
||||||
.consultation-center {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 120px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.consultation-error {
|
|
||||||
font-size: var(--tk-font-cap);
|
|
||||||
color: $dan;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── 空状态 ─── */
|
|
||||||
.consultation-empty {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 120px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-icon {
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: $pri-l;
|
|
||||||
@include flex-center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-char {
|
|
||||||
@include serif-number;
|
|
||||||
font-size: var(--tk-font-num);
|
|
||||||
font-weight: 700;
|
|
||||||
color: $pri;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-title {
|
|
||||||
font-size: var(--tk-font-body-sm);
|
|
||||||
font-weight: 600;
|
|
||||||
color: $tx;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-hint {
|
|
||||||
font-size: var(--tk-font-cap);
|
|
||||||
color: var(--tk-text-secondary);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── 会话列表 ─── */
|
|
||||||
.session-list {
|
.session-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-card {
|
/* ─── 已关闭会话半透明(ContentCard 已接管卡片外层) ─── */
|
||||||
|
.session-card-closed {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── 会话卡片内部布局(ContentCard 内部) ─── */
|
||||||
|
.session-inner {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
background: $card;
|
|
||||||
border-radius: $r;
|
|
||||||
padding: 16px;
|
|
||||||
box-shadow: $shadow-sm;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-card-closed {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-avatar {
|
.session-avatar {
|
||||||
@@ -159,18 +100,6 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-tag {
|
|
||||||
font-size: var(--tk-font-micro);
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: $r-xs;
|
|
||||||
font-weight: 500;
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
&.tag-ok { background: $acc-l; color: $acc; }
|
|
||||||
&.tag-warn { background: $wrn-l; color: $wrn; }
|
|
||||||
&.tag-default { background: $surface-alt; color: $tx3; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-meta {
|
.session-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -193,7 +122,7 @@
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── 未读角标 ─── */
|
/* ─── 未读角标(页面特有) ─── */
|
||||||
.session-badge {
|
.session-badge {
|
||||||
background: $dan;
|
background: $dan;
|
||||||
border-radius: $r-pill;
|
border-radius: $r-pill;
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ import Taro, { useReachBottom } from '@tarojs/taro';
|
|||||||
import { usePageData } from '@/hooks/usePageData';
|
import { usePageData } from '@/hooks/usePageData';
|
||||||
import { useAuthStore } from '@/stores/auth';
|
import { useAuthStore } from '@/stores/auth';
|
||||||
import { listConsultations, ConsultationSession } from '@/services/consultation';
|
import { listConsultations, ConsultationSession } from '@/services/consultation';
|
||||||
import Loading from '../../components/Loading';
|
import PageShell from '@/components/ui/PageShell';
|
||||||
import GuestGuard from '../../components/GuestGuard';
|
import ContentCard from '@/components/ui/ContentCard';
|
||||||
|
import StatusTag from '@/components/ui/StatusTag';
|
||||||
|
import LoadingCard from '@/components/ui/LoadingCard';
|
||||||
|
import EmptyState from '@/components/EmptyState';
|
||||||
|
import ErrorState from '@/components/ErrorState';
|
||||||
|
import Loading from '@/components/Loading';
|
||||||
|
import GuestGuard from '@/components/GuestGuard';
|
||||||
import { useElderClass } from '../../hooks/useElderClass';
|
import { useElderClass } from '../../hooks/useElderClass';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
function getStatusTag(status: string) {
|
|
||||||
if (status === 'active') return { label: '进行中', cls: 'tag-ok' };
|
|
||||||
if (status === 'pending') return { label: '等待接诊', cls: 'tag-warn' };
|
|
||||||
return { label: { closed: '已结束', cancelled: '已取消' }[status] || status, cls: 'tag-default' };
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatTime(iso: string): string {
|
function formatTime(iso: string): string {
|
||||||
if (!iso) return '';
|
if (!iso) return '';
|
||||||
const d = new Date(iso);
|
const d = new Date(iso);
|
||||||
@@ -33,6 +33,22 @@ function formatTime(iso: string): string {
|
|||||||
return `${m}-${day}`;
|
return `${m}-${day}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 咨询状态到 StatusTag status 的映射 */
|
||||||
|
function getConsultStatus(status: string): string {
|
||||||
|
if (status === 'active') return 'active';
|
||||||
|
if (status === 'pending') return 'pending';
|
||||||
|
if (status === 'closed') return 'completed';
|
||||||
|
if (status === 'cancelled') return 'cancelled';
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
const STATUS_LABEL_MAP: Record<string, string> = {
|
||||||
|
active: '进行中',
|
||||||
|
pending: '等待接诊',
|
||||||
|
closed: '已结束',
|
||||||
|
cancelled: '已取消',
|
||||||
|
};
|
||||||
|
|
||||||
export default function Consultation() {
|
export default function Consultation() {
|
||||||
const user = useAuthStore((s) => s.user);
|
const user = useAuthStore((s) => s.user);
|
||||||
const [sessions, setSessions] = useState<ConsultationSession[]>([]);
|
const [sessions, setSessions] = useState<ConsultationSession[]>([]);
|
||||||
@@ -60,6 +76,7 @@ export default function Consultation() {
|
|||||||
setSessions([]);
|
setSessions([]);
|
||||||
setTotal(0);
|
setTotal(0);
|
||||||
}
|
}
|
||||||
|
setError('加载失败,请稍后重试');
|
||||||
Taro.showToast({ title: '加载失败,下拉重试', icon: 'none' });
|
Taro.showToast({ title: '加载失败,下拉重试', icon: 'none' });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -85,52 +102,59 @@ export default function Consultation() {
|
|||||||
Taro.navigateTo({ url: `/pages/pkg-consultation/detail/index?id=${session.id}` });
|
Taro.navigateTo({ url: `/pages/pkg-consultation/detail/index?id=${session.id}` });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
if (!user) {
|
||||||
<View className={`consultation-page ${modeClass}`}>
|
return (
|
||||||
{!user ? (
|
<PageShell safeBottom className={modeClass}>
|
||||||
<GuestGuard title='请先登录' desc='登录后即可与医生在线交流' />
|
<GuestGuard title='请先登录' desc='登录后即可与医生在线交流' />
|
||||||
|
</PageShell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading && sessions.length === 0) {
|
||||||
|
return <LoadingCard count={4} layout="list" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error && sessions.length === 0) {
|
||||||
|
return (
|
||||||
|
<PageShell safeBottom className={modeClass}>
|
||||||
|
<ErrorState text={error} onRetry={() => loadSessions(1, true)} />
|
||||||
|
</PageShell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageShell safeBottom className={modeClass}>
|
||||||
|
{/* 副标题 */}
|
||||||
|
<Text className='consultation-subtitle'>随时随地,连接专业医生</Text>
|
||||||
|
|
||||||
|
{/* 发起咨询按钮 */}
|
||||||
|
<View
|
||||||
|
className='consultation-create-btn'
|
||||||
|
onClick={() => Taro.navigateTo({ url: '/pages/consultation/create/index' })}
|
||||||
|
>
|
||||||
|
<Text className='consultation-create-btn-text'>发起咨询</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 会话列表 */}
|
||||||
|
{sessions.length === 0 ? (
|
||||||
|
<EmptyState
|
||||||
|
icon='问'
|
||||||
|
text='暂无咨询记录'
|
||||||
|
hint='发起咨询后即可在这里与医生交流'
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<View className='consultation-body'>
|
<View className='session-list'>
|
||||||
{/* 副标题 */}
|
{sessions.map((session) => {
|
||||||
<Text className='consultation-subtitle'>随时随地,连接专业医生</Text>
|
const initial = (session.subject || '咨').charAt(0);
|
||||||
|
const isClosed = session.status === 'closed' || session.status === 'cancelled';
|
||||||
{/* 发起咨询按钮 — 实心主色 */}
|
return (
|
||||||
<View
|
<ContentCard
|
||||||
className='consultation-create-btn'
|
key={session.id}
|
||||||
onClick={() => Taro.navigateTo({ url: '/pages/consultation/create/index' })}
|
className={isClosed ? 'session-card-closed' : ''}
|
||||||
>
|
activeFeedback="opacity"
|
||||||
<Text className='consultation-create-btn-text'>发起咨询</Text>
|
onPress={() => handleTapSession(session)}
|
||||||
</View>
|
>
|
||||||
|
<View className='session-inner'>
|
||||||
{/* 内容区 */}
|
|
||||||
{loading ? (
|
|
||||||
<View className='consultation-center'>
|
|
||||||
<Loading text='加载中...' />
|
|
||||||
</View>
|
|
||||||
) : error ? (
|
|
||||||
<View className='consultation-center'>
|
|
||||||
<Text className='consultation-error'>{error}</Text>
|
|
||||||
</View>
|
|
||||||
) : sessions.length === 0 ? (
|
|
||||||
<View className='consultation-empty'>
|
|
||||||
<View className='empty-icon'>
|
|
||||||
<Text className='empty-char'>问</Text>
|
|
||||||
</View>
|
|
||||||
<Text className='empty-title'>暂无咨询记录</Text>
|
|
||||||
<Text className='empty-hint'>发起咨询后即可在这里与医生交流</Text>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<View className='session-list'>
|
|
||||||
{sessions.map((session) => {
|
|
||||||
const tag = getStatusTag(session.status);
|
|
||||||
const initial = (session.subject || '咨').charAt(0);
|
|
||||||
const isClosed = session.status === 'closed' || session.status === 'cancelled';
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
key={session.id}
|
|
||||||
className={`session-card ${isClosed ? 'session-card-closed' : ''}`}
|
|
||||||
onClick={() => handleTapSession(session)}
|
|
||||||
>
|
|
||||||
<View className='session-avatar'>
|
<View className='session-avatar'>
|
||||||
<Text className='session-avatar-char'>{initial}</Text>
|
<Text className='session-avatar-char'>{initial}</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -146,7 +170,9 @@ export default function Consultation() {
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='session-meta'>
|
<View className='session-meta'>
|
||||||
<Text className={`session-tag ${tag.cls}`}>{tag.label}</Text>
|
<StatusTag status={getConsultStatus(session.status)} size="sm">
|
||||||
|
{STATUS_LABEL_MAP[session.status] || session.status}
|
||||||
|
</StatusTag>
|
||||||
</View>
|
</View>
|
||||||
<View className='session-message-row'>
|
<View className='session-message-row'>
|
||||||
<Text className='session-message'>
|
<Text className='session-message'>
|
||||||
@@ -162,12 +188,13 @@ export default function Consultation() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
</ContentCard>
|
||||||
})}
|
);
|
||||||
</View>
|
})}
|
||||||
)}
|
</View>
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
|
||||||
|
{loading && sessions.length > 0 && <Loading />}
|
||||||
|
</PageShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user