Files
soft/frontend-doctor/src/pages/patients/PatientListPage.tsx
MingNian d6a432aec4 feat: extract secrets to .env, remove hardcoded credentials
- Backend: .env file for DB/JWT/Redis/MinIO config, appsettings.json cleared
- Backend: Program.cs loads .env at startup (no extra NuGet packages)
- Frontend: .env files for VITE_API_URL, api-clients use import.meta.env
- Added vite-env.d.ts type declarations for both frontends
- All hardcoded localhost:5000 replaced with env variable
- Added .env.example template for onboarding
2026-05-22 22:02:08 +08:00

79 lines
3.7 KiB
TypeScript

import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { api } from '../../services/api-client';
interface Patient {
id: string; name: string; phone: string; gender: string;
medicalHistory: string[]; stentDate: string;
}
export function PatientListPage() {
const [patients, setPatients] = useState<Patient[]>([]);
const [search, setSearch] = useState('');
const [loading, setLoading] = useState(true);
useEffect(() => {
api.get<Patient[]>('/api/patients')
.then((r) => setPatients(r.data))
.catch(() => {})
.finally(() => setLoading(false));
}, []);
const filtered = patients.filter((p) =>
!search || p.name.includes(search) || p.phone.includes(search)
);
return (
<div style={{ padding: 28 }}>
<h2 style={{ marginBottom: 6, fontSize: 20, fontWeight: 700, color: '#1A1D28' }}></h2>
<p style={{ marginBottom: 18, fontSize: 13, color: '#9BA0B4' }}> {patients.length} </p>
<input value={search} onChange={(e) => setSearch(e.target.value)} placeholder="搜索姓名或手机号..."
style={{
width: 280, padding: '10px 14px', border: '1.5px solid #E1E5ED', borderRadius: 10,
fontSize: 13, marginBottom: 18, outline: 'none', boxSizing: 'border-box',
transition: 'border-color 0.2s',
}}
onFocus={(e) => e.currentTarget.style.borderColor = '#4F6EF7'}
onBlur={(e) => e.currentTarget.style.borderColor = '#E1E5ED'} />
{loading ? <div style={{ color: '#9BA0B4' }}>...</div> : (
<div style={{ background: '#fff', borderRadius: 16, boxShadow: '0 2px 12px rgba(0,0,0,0.04)', overflow: 'hidden' }}>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
<thead>
<tr style={{ borderBottom: '2px solid #F0F2F5', textAlign: 'left', background: '#F9FAFC' }}>
<th style={{ padding: '13px 20px', fontWeight: 600, color: '#5A6072' }}></th>
<th style={{ padding: '13px 20px', fontWeight: 600, color: '#5A6072' }}></th>
<th style={{ padding: '13px 20px', fontWeight: 600, color: '#5A6072' }}></th>
<th style={{ padding: '13px 20px', fontWeight: 600, color: '#5A6072' }}></th>
<th style={{ padding: '13px 20px', fontWeight: 600, color: '#5A6072' }}></th>
<th style={{ padding: '13px 20px', fontWeight: 600, color: '#5A6072' }}></th>
</tr>
</thead>
<tbody>
{filtered.map((p) => (
<tr key={p.id} style={{ borderBottom: '1px solid #F5F6F9' }}>
<td style={{ padding: '12px 20px', fontWeight: 500 }}>{p.name}</td>
<td style={{ padding: '12px 20px', color: '#9BA0B4' }}>{p.phone}</td>
<td style={{ padding: '12px 20px' }}>{p.gender || '-'}</td>
<td style={{ padding: '12px 20px', color: '#5A6072' }}>{(p.medicalHistory || []).slice(0, 3).join('、') || '-'}</td>
<td style={{ padding: '12px 20px', color: '#5A6072' }}>{p.stentDate || '-'}</td>
<td style={{ padding: '12px 20px' }}>
<Link to={`/patients/${p.id}`} style={{
color: '#4F6EF7', fontSize: 12, fontWeight: 600,
padding: '4px 12px', background: '#EDF0FD', borderRadius: 6,
}}></Link>
</td>
</tr>
))}
{filtered.length === 0 && (
<tr><td colSpan={6} style={{ padding: 32, textAlign: 'center', color: '#9BA0B4' }}></td></tr>
)}
</tbody>
</table>
</div>
)}
</div>
);
}