refactor: patient frontend UI overhaul
- Reworked design system (variables, global styles, component CSS) - Updated TabBar with icon-based navigation - Redesigned HomePage, HealthHub, ServicesHub layouts - Improved Exercise/Diet, Medication, Profile pages styling - Simplified constants (removed emoji icons, streamlined data) - Fixed launch.json cwd paths for frontend projects
This commit is contained in:
@@ -17,24 +17,26 @@
|
||||
font-size: var(--font-size-sm);
|
||||
background: var(--color-bg-secondary);
|
||||
color: var(--color-text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.active { background: var(--color-primary-bg); color: var(--color-primary); }
|
||||
.active { background: var(--color-primary-bg); color: var(--color-primary); font-weight: 600; }
|
||||
|
||||
.docCard { margin-bottom: 8px; }
|
||||
|
||||
.docHeader { display: flex; gap: 12px; margin-bottom: 12px; }
|
||||
|
||||
.avatar {
|
||||
width: 48px; height: 48px;
|
||||
border-radius: 50%;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 16px;
|
||||
background: var(--color-primary-bg);
|
||||
color: var(--color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--font-size-lg);
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -49,7 +51,8 @@
|
||||
}
|
||||
|
||||
.onlineDot {
|
||||
width: 8px; height: 8px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--color-success);
|
||||
}
|
||||
@@ -63,7 +66,7 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--color-border-light);
|
||||
border-top: 1px solid var(--color-divider);
|
||||
}
|
||||
|
||||
.fee { font-size: var(--font-size-sm); color: var(--color-danger); font-weight: 600; }
|
||||
|
||||
@@ -28,7 +28,7 @@ export function DoctorListPage() {
|
||||
))}
|
||||
</div>
|
||||
{doctors.length === 0 ? (
|
||||
<Empty icon="👨⚕️" message="暂无医生" />
|
||||
<Empty message="暂无医生" />
|
||||
) : (
|
||||
doctors.map((doc) => (
|
||||
<Card key={doc.id} className={styles.docCard}>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.tabs { display: flex; gap: 8px; margin-bottom: 14px; }
|
||||
.tab { padding: 6px 14px; border-radius: var(--radius-full); font-size: var(--font-size-sm); background: var(--color-bg-secondary); color: var(--color-text-secondary); }
|
||||
.tab { padding: 6px 14px; border-radius: var(--radius-full); font-size: var(--font-size-sm); background: var(--color-bg-secondary); color: var(--color-text-secondary); font-weight: 500; }
|
||||
.tabActive { background: var(--color-primary); color: var(--color-text-inverse); }
|
||||
.card { margin-bottom: 8px; }
|
||||
.cardHeader { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
|
||||
.title { font-size: var(--font-size-base); font-weight: 600; }
|
||||
.status { font-size: var(--font-size-xs); font-weight: 500; }
|
||||
.meta { font-size: var(--font-size-xs); color: var(--color-text-tertiary); margin-top: 2px; }
|
||||
.fab { position: fixed; bottom: 80px; right: max(16px, calc((100vw - var(--max-content-width)) / 2 + 16px)); padding: 12px 20px; background: var(--color-primary); color: var(--color-text-inverse); border-radius: var(--radius-full); font-weight: 600; box-shadow: var(--shadow-lg); z-index: 50; }
|
||||
.fab { position: fixed; bottom: 80px; right: max(16px, calc((100vw - var(--max-content-width)) / 2 + 16px)); padding: 12px 20px; background: var(--color-primary-gradient); color: var(--color-text-inverse); border-radius: var(--radius-full); font-weight: 600; box-shadow: 0 4px 16px rgba(79,110,247,0.35); z-index: 50; }
|
||||
|
||||
@@ -33,7 +33,7 @@ export function FollowUpListPage() {
|
||||
<button className={`${styles.tab} ${tab === 'completed' ? styles.tabActive : ''}`} onClick={() => setTab('completed')}>已完成</button>
|
||||
</div>
|
||||
{filtered.length === 0 ? (
|
||||
<Empty icon="🏥" message="暂无复查计划" />
|
||||
<Empty message="暂无复查计划" />
|
||||
) : (
|
||||
filtered.map((f) => (
|
||||
<Card key={f.id} className={styles.card}>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.tabs { display: flex; gap: 8px; margin-bottom: 14px; }
|
||||
.tab { padding: 6px 14px; border-radius: var(--radius-full); font-size: var(--font-size-sm); background: var(--color-bg-secondary); color: var(--color-text-secondary); }
|
||||
.tab { padding: 6px 14px; border-radius: var(--radius-full); font-size: var(--font-size-sm); background: var(--color-bg-secondary); color: var(--color-text-secondary); font-weight: 500; }
|
||||
.tabActive { background: var(--color-primary); color: var(--color-text-inverse); }
|
||||
.card { margin-bottom: 8px; }
|
||||
.cardHeader { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
|
||||
.cardTitle { font-size: var(--font-size-base); font-weight: 600; }
|
||||
.cardMeta { display: flex; justify-content: space-between; font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
||||
.fab { position: fixed; bottom: 80px; right: max(16px, calc((100vw - var(--max-content-width)) / 2 + 16px)); padding: 12px 20px; background: var(--color-primary); color: var(--color-text-inverse); border-radius: var(--radius-full); font-weight: 600; box-shadow: var(--shadow-lg); z-index: 50; }
|
||||
.fab { position: fixed; bottom: 80px; right: max(16px, calc((100vw - var(--max-content-width)) / 2 + 16px)); padding: 12px 20px; background: var(--color-primary-gradient); color: var(--color-text-inverse); border-radius: var(--radius-full); font-weight: 600; box-shadow: 0 4px 16px rgba(79,110,247,0.35); z-index: 50; }
|
||||
|
||||
@@ -41,7 +41,7 @@ export function ReportListPage() {
|
||||
))}
|
||||
</div>
|
||||
{filtered.length === 0 ? (
|
||||
<Empty icon="📋" message="暂无报告" />
|
||||
<Empty message="暂无报告" />
|
||||
) : (
|
||||
filtered.map((r) => (
|
||||
<Card key={r.id} className={styles.card} onClick={() => navigate(`/services/reports/${r.id}`)}>
|
||||
|
||||
@@ -74,7 +74,9 @@ export function ReportUploadPage() {
|
||||
</div>
|
||||
|
||||
<div className={styles.uploadArea} onClick={() => fileRef.current?.click()}>
|
||||
<span style={{ fontSize: 36 }}>📷</span>
|
||||
<div style={{ width: 72, height: 72, borderRadius: 18, background: 'var(--color-bg-secondary)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 12px' }}>
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#9BA0B4" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
||||
</div>
|
||||
<span style={{ fontSize: 14, color: '#6B7280' }}>点击选择报告图片</span>
|
||||
<span style={{ fontSize: 11, color: '#9CA3AF' }}>{files.length > 0 ? `已选 ${files.length} 个文件` : '支持 jpg、png、pdf,可多选'}</span>
|
||||
</div>
|
||||
@@ -85,9 +87,9 @@ export function ReportUploadPage() {
|
||||
{files.map((file, i) => (
|
||||
<div key={i} className={styles.fileItem}>
|
||||
<span className={styles.fileName}>
|
||||
{file.type.startsWith('image/') ? '🖼' : '📄'} {file.name} ({(file.size / 1024).toFixed(0)}KB)
|
||||
{file.name} ({(file.size / 1024).toFixed(0)}KB)
|
||||
</span>
|
||||
<button className={styles.fileRemove} onClick={() => removeFile(i)}>✕</button>
|
||||
<button className={styles.fileRemove} onClick={() => removeFile(i)} style={{ fontWeight: 700 }}>×</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -6,18 +6,27 @@
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
padding: 20px;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 20px 18px;
|
||||
background: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
border-radius: var(--radius-xl);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.15s;
|
||||
transition: transform 0.2s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.card:active { transform: scale(0.98); }
|
||||
.icon { font-size: 36px; }
|
||||
.label { font-size: var(--font-size-md); font-weight: 600; }
|
||||
.desc { font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
||||
|
||||
.icon {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.label { font-size: var(--font-size-md); font-weight: 700; color: var(--color-text-primary); margin-bottom: 4px; }
|
||||
.desc { font-size: 12px; color: var(--color-text-tertiary); }
|
||||
|
||||
@@ -3,9 +3,44 @@ import { PageHeader } from '@/components/layout/PageHeader';
|
||||
import styles from './ServicesHubPage.module.css';
|
||||
|
||||
const SERVICES = [
|
||||
{ label: '在线问诊', icon: '👨⚕️', desc: '图文咨询医生', path: '/services/consultation' },
|
||||
{ label: '报告解读', icon: '📋', desc: '上传检查报告', path: '/services/reports' },
|
||||
{ label: '复查管理', icon: '🏥', desc: '管理复查计划', path: '/services/follow-ups' },
|
||||
{
|
||||
label: '在线问诊',
|
||||
desc: '图文咨询医生',
|
||||
path: '/services/consultation',
|
||||
svg: (
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#4F6EF7" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="12" cy="7" r="4" />
|
||||
</svg>
|
||||
),
|
||||
bg: '#E6F0FF',
|
||||
},
|
||||
{
|
||||
label: '报告解读',
|
||||
desc: '上传检查报告',
|
||||
path: '/services/reports',
|
||||
svg: (
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#845EF7" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
<line x1="16" y1="13" x2="8" y2="13" />
|
||||
<line x1="16" y1="17" x2="8" y2="17" />
|
||||
</svg>
|
||||
),
|
||||
bg: '#F3E8FF',
|
||||
},
|
||||
{
|
||||
label: '复查管理',
|
||||
desc: '管理复查计划',
|
||||
path: '/services/follow-ups',
|
||||
svg: (
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#EF4444" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M18 10h-2.5L14 13l-2-6-3 6.5L7 10H5" />
|
||||
<rect x="2" y="2" width="20" height="20" rx="3" />
|
||||
</svg>
|
||||
),
|
||||
bg: '#FEE9E9',
|
||||
},
|
||||
];
|
||||
|
||||
export function ServicesHubPage() {
|
||||
@@ -17,9 +52,11 @@ export function ServicesHubPage() {
|
||||
<div className={styles.grid}>
|
||||
{SERVICES.map((s) => (
|
||||
<button key={s.label} className={styles.card} onClick={() => navigate(s.path)}>
|
||||
<span className={styles.icon}>{s.icon}</span>
|
||||
<span className={styles.label}>{s.label}</span>
|
||||
<span className={styles.desc}>{s.desc}</span>
|
||||
<span className={styles.icon} style={{ background: s.bg }}>{s.svg}</span>
|
||||
<div>
|
||||
<div className={styles.label}>{s.label}</div>
|
||||
<div className={styles.desc}>{s.desc}</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user