fix(mp): 小程序页面优化 + E2E 测试报告更新
- 小程序各页面优化和修复 - 更新联调报告和 E2E 测试报告 - 更新 miniprogram wiki
This commit is contained in:
67
apps/miniprogram/src/components/SegmentTabs/index.scss
Normal file
67
apps/miniprogram/src/components/SegmentTabs/index.scss
Normal file
@@ -0,0 +1,67 @@
|
||||
@import '../../styles/variables.scss';
|
||||
|
||||
.seg-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&--underline {
|
||||
border-bottom: 1px solid $bd-l;
|
||||
|
||||
.seg-tab {
|
||||
flex: 1;
|
||||
height: var(--tk-touch-min);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
&--active {
|
||||
.seg-tab__text {
|
||||
color: $pri;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 30%;
|
||||
right: 30%;
|
||||
height: 4px;
|
||||
background: $pri;
|
||||
border-radius: $r-xs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.seg-tab__text {
|
||||
font-size: var(--tk-font-body-lg);
|
||||
color: $tx2;
|
||||
}
|
||||
}
|
||||
|
||||
&--pill {
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.seg-tab {
|
||||
padding: 8px 24px;
|
||||
border-radius: $r-pill;
|
||||
background: $surface-alt;
|
||||
|
||||
&--active {
|
||||
background: $pri;
|
||||
|
||||
.seg-tab__text {
|
||||
color: $card;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.seg-tab__text {
|
||||
font-size: var(--tk-font-body-lg);
|
||||
color: $tx2;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
apps/miniprogram/src/components/SegmentTabs/index.tsx
Normal file
36
apps/miniprogram/src/components/SegmentTabs/index.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { View, Text } from '@tarojs/components';
|
||||
import './index.scss';
|
||||
|
||||
interface Tab {
|
||||
key: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface SegmentTabsProps {
|
||||
tabs: Tab[];
|
||||
activeKey: string;
|
||||
onChange: (key: string) => void;
|
||||
variant?: 'underline' | 'pill';
|
||||
}
|
||||
|
||||
export default React.memo(function SegmentTabs({
|
||||
tabs,
|
||||
activeKey,
|
||||
onChange,
|
||||
variant = 'underline',
|
||||
}: SegmentTabsProps) {
|
||||
return (
|
||||
<View className={`seg-tabs seg-tabs--${variant}`}>
|
||||
{tabs.map((tab) => (
|
||||
<View
|
||||
key={tab.key}
|
||||
className={`seg-tab ${activeKey === tab.key ? 'seg-tab--active' : ''}`}
|
||||
onClick={() => onChange(tab.key)}
|
||||
>
|
||||
<Text className='seg-tab__text'>{tab.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
});
|
||||
@@ -57,7 +57,6 @@
|
||||
font-weight: bold;
|
||||
color: $tx;
|
||||
line-height: 1.4;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -4,6 +4,7 @@ import Taro, { useReachBottom } from '@tarojs/taro';
|
||||
import { usePageData } from '@/hooks/usePageData';
|
||||
import { listArticles, listCategories, Article, ArticleCategory } from '../../services/article';
|
||||
import EmptyState from '../../components/EmptyState';
|
||||
import ErrorState from '../../components/ErrorState';
|
||||
import Loading from '../../components/Loading';
|
||||
import { useElderClass } from '../../hooks/useElderClass';
|
||||
import './index.scss';
|
||||
@@ -14,6 +15,7 @@ export default function ArticleList() {
|
||||
const [page, setPage] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [categories, setCategories] = useState<ArticleCategory[]>([]);
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(null);
|
||||
|
||||
@@ -28,6 +30,7 @@ export default function ArticleList() {
|
||||
|
||||
const fetchData = useCallback(async (p: number, append = false, categoryId?: string | null) => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const cid = categoryId !== undefined ? categoryId : activeCategory;
|
||||
const res = await listArticles({
|
||||
@@ -39,6 +42,7 @@ export default function ArticleList() {
|
||||
setTotal(res.total);
|
||||
setPage(p);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -93,7 +97,9 @@ export default function ArticleList() {
|
||||
)}
|
||||
|
||||
<View className='article-list'>
|
||||
{articles.map((a) => (
|
||||
{error ? (
|
||||
<ErrorState onRetry={() => fetchData(1, false, null)} />
|
||||
) : articles.map((a) => (
|
||||
<View
|
||||
className='article-card'
|
||||
key={a.id}
|
||||
|
||||
@@ -5,7 +5,9 @@ import { useAuthStore } from '../../stores/auth';
|
||||
import { useElderClass } from '../../hooks/useElderClass';
|
||||
import { findThreshold, inputVitalSign, type HealthThreshold } from '../../services/health';
|
||||
import Loading from '../../components/Loading';
|
||||
import ErrorState from '../../components/ErrorState';
|
||||
import GuestGuard from '../../components/GuestGuard';
|
||||
import SegmentTabs from '../../components/SegmentTabs';
|
||||
import { useHealthData, VITAL_TABS, type VitalType } from './useHealthData';
|
||||
import './index.scss';
|
||||
|
||||
@@ -28,8 +30,8 @@ export default function Health() {
|
||||
const currentPatient = useAuthStore((s) => s.currentPatient);
|
||||
const modeClass = useElderClass();
|
||||
const {
|
||||
user, todaySummary, loading, activeTab, trendData, trendLoading,
|
||||
aiSuggestions, thresholds, handleTabChange, loadTrend, refreshToday,
|
||||
user, todaySummary, loading, error, activeTab, trendData, trendLoading,
|
||||
aiSuggestions, thresholds, handleTabChange, loadTrend, refreshToday, fetchData,
|
||||
} = useHealthData();
|
||||
|
||||
const [systolic, setSystolic] = useState('');
|
||||
@@ -44,6 +46,17 @@ export default function Health() {
|
||||
return <GuestGuard title='请先登录' desc='登录后即可记录和查看健康数据' />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<View className={`health-page ${modeClass}`}>
|
||||
<View className='health-header'>
|
||||
<Text className='health-title'>健康数据</Text>
|
||||
</View>
|
||||
<ErrorState onRetry={fetchData} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const getWarnStatus = (type: VitalType): string | null => {
|
||||
if (type === 'blood_pressure') {
|
||||
const sys = parseFloat(systolic);
|
||||
@@ -184,24 +197,7 @@ export default function Health() {
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View className='vital-tabs'>
|
||||
{VITAL_TABS.map((tab) => {
|
||||
const hasData = tab.key === 'blood_pressure' ? !!todaySummary?.blood_pressure
|
||||
: tab.key === 'heart_rate' ? !!todaySummary?.heart_rate
|
||||
: tab.key === 'blood_sugar' ? !!todaySummary?.blood_sugar
|
||||
: !!todaySummary?.weight;
|
||||
return (
|
||||
<View
|
||||
key={tab.key}
|
||||
className={`vital-tab ${activeTab === tab.key ? 'vital-tab-active' : ''}`}
|
||||
onClick={() => handleTabChange(tab.key)}
|
||||
>
|
||||
<Text className='vital-tab-text'>{tab.label}</Text>
|
||||
{!hasData && <View className='vital-tab-dot' />}
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
<SegmentTabs tabs={VITAL_TABS} activeKey={activeTab} onChange={handleTabChange} variant="pill" />
|
||||
|
||||
<View className='input-section'>
|
||||
{activeTab === 'blood_pressure' && (
|
||||
|
||||
@@ -31,6 +31,7 @@ export function useHealthData() {
|
||||
const [trendLoading, setTrendLoading] = useState(false);
|
||||
const [aiSuggestions, setAiSuggestions] = useState<AiSuggestionItem[]>([]);
|
||||
const [thresholds, setThresholds] = useState<HealthThreshold[]>(DEFAULT_THRESHOLDS);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const loadTrend = async (type: VitalType) => {
|
||||
setTrendLoading(true);
|
||||
@@ -60,12 +61,15 @@ export function useHealthData() {
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
await Promise.allSettled([
|
||||
setError(false);
|
||||
const results = await Promise.allSettled([
|
||||
refreshToday(),
|
||||
loadTrend(activeTab),
|
||||
loadAiSuggestions(),
|
||||
getHealthThresholds().then((t) => { if (t.length > 0) setThresholds(t); }),
|
||||
]);
|
||||
const hasError = results.some((r) => r.status === 'rejected');
|
||||
if (hasError) setError(true);
|
||||
};
|
||||
|
||||
usePageData(fetchData, {
|
||||
@@ -83,6 +87,7 @@ export function useHealthData() {
|
||||
user,
|
||||
todaySummary,
|
||||
loading,
|
||||
error,
|
||||
activeTab,
|
||||
trendData,
|
||||
trendLoading,
|
||||
@@ -92,5 +97,6 @@ export function useHealthData() {
|
||||
loadTrend,
|
||||
refreshToday,
|
||||
fetchTrend,
|
||||
fetchData,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import './index.scss';
|
||||
|
||||
const IS_DEV = process.env.NODE_ENV !== 'production';
|
||||
|
||||
// 运行时检测是否在 DevTools 模拟器中(弥补编译时 IS_DEV 在 production 构建中为 false 的问题)
|
||||
const IS_SIMULATOR = typeof __wxConfig !== 'undefined' && (__wxConfig as any).envVersion !== 'release';
|
||||
|
||||
export default function Login() {
|
||||
const modeClass = useElderClass();
|
||||
const [needBind, setNeedBind] = useState(false);
|
||||
@@ -124,7 +127,7 @@ export default function Login() {
|
||||
>
|
||||
授权手机号完成绑定
|
||||
</Button>
|
||||
{IS_DEV && (
|
||||
{(IS_DEV || IS_SIMULATOR) && (
|
||||
<Button className='login-btn login-btn--dev' onClick={handleDevQuickLogin} loading={loading}>
|
||||
开发模式快速登录
|
||||
</Button>
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { PointsProduct } from '../../services/points';
|
||||
import { useAuthStore } from '../../stores/auth';
|
||||
import { usePointsStore } from '../../stores/points';
|
||||
import Loading from '../../components/Loading';
|
||||
import ErrorState from '../../components/ErrorState';
|
||||
import { useElderClass } from '../../hooks/useElderClass';
|
||||
import './index.scss';
|
||||
|
||||
@@ -37,11 +38,13 @@ export default function Mall() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [checkinLoading, setCheckinLoading] = useState(false);
|
||||
const [noProfile, setNoProfile] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const modeClass = useElderClass();
|
||||
|
||||
const fetchProducts = useCallback(
|
||||
async (pageNum: number, type: string, isRefresh = false) => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await listProducts({
|
||||
page: pageNum,
|
||||
@@ -57,6 +60,7 @@ export default function Mall() {
|
||||
setTotal(res.total);
|
||||
setPage(pageNum);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -188,7 +192,9 @@ export default function Mall() {
|
||||
</View>
|
||||
|
||||
{/* 商品列表 */}
|
||||
{products.length === 0 && !loading ? (
|
||||
{error ? (
|
||||
<ErrorState onRetry={() => loadAll()} />
|
||||
) : products.length === 0 && !loading ? (
|
||||
<View className='mall-empty-state'>
|
||||
<View className='empty-icon'>
|
||||
<Text className='empty-char'>礼</Text>
|
||||
|
||||
@@ -4,6 +4,7 @@ import Taro, { useReachBottom } from '@tarojs/taro';
|
||||
import { listConsultations, ConsultationSession } from '../../services/consultation';
|
||||
import { notificationService } from '../../services/notification';
|
||||
import Loading from '../../components/Loading';
|
||||
import ErrorState from '../../components/ErrorState';
|
||||
import GuestGuard from '../../components/GuestGuard';
|
||||
import { useAuthStore } from '../../stores/auth';
|
||||
import { useElderClass } from '../../hooks/useElderClass';
|
||||
@@ -36,11 +37,13 @@ export default function Messages() {
|
||||
const [sessions, setSessions] = useState<ConsultationSession[]>([]);
|
||||
const [notifications, setNotifications] = useState<NotificationItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
|
||||
const loadData = useCallback(async (tab: MsgTab, pageNum: number = 1, isRefresh = false) => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
if (tab === 'consultation') {
|
||||
const res = await listConsultations({ page: pageNum, page_size: 20 });
|
||||
@@ -63,11 +66,12 @@ export default function Messages() {
|
||||
}
|
||||
setPage(pageNum);
|
||||
} catch {
|
||||
setError(true);
|
||||
if (isRefresh) {
|
||||
if (tab === 'consultation') setSessions([]);
|
||||
else setNotifications([]);
|
||||
Taro.showToast({ title: '加载失败,下拉重试', icon: 'none' });
|
||||
}
|
||||
Taro.showToast({ title: '加载失败,下拉重试', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -139,6 +143,10 @@ export default function Messages() {
|
||||
</View>
|
||||
|
||||
<View className='msg-content'>
|
||||
{error ? (
|
||||
<ErrorState onRetry={() => loadData(activeTab, 1, true)} />
|
||||
) : (
|
||||
<>
|
||||
{/* 咨询列表 */}
|
||||
{activeTab === 'consultation' && (
|
||||
loading ? (
|
||||
@@ -220,6 +228,8 @@ export default function Messages() {
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,9 @@ import Taro from '@tarojs/taro';
|
||||
import { usePageData } from '@/hooks/usePageData';
|
||||
import { listAlerts, type Alert } from '@/services/doctor/alerts';
|
||||
import Loading from '@/components/Loading';
|
||||
import ErrorState from '@/components/ErrorState';
|
||||
import EmptyState from '@/components/EmptyState';
|
||||
import SegmentTabs from '@/components/SegmentTabs';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import { safeNavigateTo } from '@/utils/navigate';
|
||||
import './index.scss';
|
||||
@@ -34,6 +36,7 @@ export default function AlertList() {
|
||||
const modeClass = useElderClass();
|
||||
const [alerts, setAlerts] = useState<Alert[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState('');
|
||||
const [total, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
@@ -43,6 +46,7 @@ export default function AlertList() {
|
||||
|
||||
const loadAlerts = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await listAlerts({
|
||||
status: activeTab || undefined,
|
||||
@@ -52,6 +56,7 @@ export default function AlertList() {
|
||||
setAlerts(res.data || []);
|
||||
setTotal(res.total || 0);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -91,6 +96,28 @@ export default function AlertList() {
|
||||
|
||||
if (loading && alerts.length === 0) return <Loading />;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ScrollView scrollY className={`alert-list-page ${modeClass}`}>
|
||||
<View className='alert-list-header'>
|
||||
<Text className='alert-list-title'>告警列表</Text>
|
||||
</View>
|
||||
<View className='alert-tabs'>
|
||||
{STATUS_TABS.map((tab) => (
|
||||
<Text
|
||||
key={tab.value}
|
||||
className={`alert-tab ${activeTab === tab.value ? 'alert-tab--active' : ''}`}
|
||||
onClick={() => handleTabChange(tab.value)}
|
||||
>
|
||||
{tab.label}
|
||||
</Text>
|
||||
))}
|
||||
</View>
|
||||
<ErrorState onRetry={loadAlerts} />
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollView scrollY className={`alert-list-page ${modeClass}`}>
|
||||
<View className='alert-list-header'>
|
||||
@@ -98,17 +125,7 @@ export default function AlertList() {
|
||||
<Text className='alert-list-count'>共 {total} 条</Text>
|
||||
</View>
|
||||
|
||||
<View className='alert-tabs'>
|
||||
{STATUS_TABS.map((tab) => (
|
||||
<Text
|
||||
key={tab.value}
|
||||
className={`alert-tab ${activeTab === tab.value ? 'alert-tab--active' : ''}`}
|
||||
onClick={() => handleTabChange(tab.value)}
|
||||
>
|
||||
{tab.label}
|
||||
</Text>
|
||||
))}
|
||||
</View>
|
||||
<SegmentTabs tabs={STATUS_TABS.map(t => ({ key: t.value, label: t.label }))} activeKey={activeTab} onChange={handleTabChange} variant="pill" />
|
||||
|
||||
{alerts.length === 0 ? (
|
||||
<EmptyState description='暂无告警' />
|
||||
|
||||
@@ -5,10 +5,12 @@ import { usePageData } from '@/hooks/usePageData';
|
||||
import { listDialysisRecords, type DialysisRecord } from '@/services/doctor/dialysis';
|
||||
import { listPatients } from '@/services/doctor/patient';
|
||||
import Loading from '@/components/Loading';
|
||||
import ErrorState from '@/components/ErrorState';
|
||||
import EmptyState from '@/components/EmptyState';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import { safeNavigateTo } from '@/utils/navigate';
|
||||
import './index.scss';
|
||||
import SegmentTabs from '@/components/SegmentTabs';
|
||||
import './index.scss';;
|
||||
|
||||
const TABS = [
|
||||
{ key: '', label: '全部' },
|
||||
@@ -28,6 +30,7 @@ export default function DialysisList() {
|
||||
const [activeTab, setActiveTab] = useState('');
|
||||
const [records, setRecords] = useState<DialysisRecord[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
const mountedRef = useRef(false);
|
||||
@@ -35,6 +38,7 @@ export default function DialysisList() {
|
||||
const loadRecords = useCallback(async (p: number) => {
|
||||
if (!currentPatientId) return;
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const params: { page: number; page_size: number; status?: string } = { page: p, page_size: 20 };
|
||||
if (activeTab) params.status = activeTab;
|
||||
@@ -43,6 +47,7 @@ export default function DialysisList() {
|
||||
setTotal(res.total || 0);
|
||||
setPage(p);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -88,6 +93,7 @@ export default function DialysisList() {
|
||||
// 服务端已按 activeTab 过滤,无需客户端二次筛选
|
||||
|
||||
if (loading && records.length === 0) return <Loading />;
|
||||
if (error) return <ErrorState onRetry={() => loadRecords(1)} />;
|
||||
|
||||
return (
|
||||
<ScrollView scrollY className={`dialysis-page ${modeClass}`}>
|
||||
@@ -104,17 +110,7 @@ export default function DialysisList() {
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View className='tabs'>
|
||||
{TABS.map((t) => (
|
||||
<View
|
||||
key={t.key}
|
||||
className={`tab ${activeTab === t.key ? 'tab--active' : ''}`}
|
||||
onClick={() => handleTab(t.key)}
|
||||
>
|
||||
<Text className='tab-text'>{t.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<SegmentTabs tabs={TABS} activeKey={activeTab} onChange={handleTab} variant="underline" />
|
||||
|
||||
{!currentPatientId ? (
|
||||
<EmptyState text='请搜索并选择患者' />
|
||||
|
||||
@@ -5,9 +5,11 @@ import { usePageData } from '@/hooks/usePageData';
|
||||
import { listDialysisPrescriptions, type DialysisPrescription } from '@/services/doctor/dialysis';
|
||||
import { listPatients } from '@/services/doctor/patient';
|
||||
import Loading from '@/components/Loading';
|
||||
import ErrorState from '@/components/ErrorState';
|
||||
import EmptyState from '@/components/EmptyState';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import { safeNavigateTo } from '@/utils/navigate';
|
||||
import SegmentTabs from '@/components/SegmentTabs';
|
||||
import './index.scss';
|
||||
|
||||
const TABS = [
|
||||
@@ -25,12 +27,14 @@ export default function PrescriptionList() {
|
||||
const [activeTab, setActiveTab] = useState('');
|
||||
const [prescriptions, setPrescriptions] = useState<DialysisPrescription[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
const mountedRef = useRef(false);
|
||||
|
||||
const loadData = useCallback(async (p: number) => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await listDialysisPrescriptions({
|
||||
patient_id: currentPatientId || undefined,
|
||||
@@ -42,6 +46,7 @@ export default function PrescriptionList() {
|
||||
setTotal(res.total || 0);
|
||||
setPage(p);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -78,6 +83,7 @@ export default function PrescriptionList() {
|
||||
};
|
||||
|
||||
if (loading && prescriptions.length === 0) return <Loading />;
|
||||
if (error) return <ErrorState onRetry={() => loadData(1)} />;
|
||||
|
||||
return (
|
||||
<ScrollView scrollY className={`prescription-page ${modeClass}`}>
|
||||
@@ -94,17 +100,7 @@ export default function PrescriptionList() {
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View className='tabs'>
|
||||
{TABS.map((t) => (
|
||||
<View
|
||||
key={t.key}
|
||||
className={`tab ${activeTab === t.key ? 'tab--active' : ''}`}
|
||||
onClick={() => { setActiveTab(t.key); setPage(1); }}
|
||||
>
|
||||
<Text className='tab-text'>{t.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<SegmentTabs tabs={TABS} activeKey={activeTab} onChange={(key) => { setActiveTab(key); setPage(1); }} variant="underline" />
|
||||
|
||||
{prescriptions.length === 0 ? (
|
||||
<EmptyState text='暂无透析处方' />
|
||||
|
||||
@@ -5,6 +5,7 @@ import { usePageData } from '@/hooks/usePageData';
|
||||
import { listLabReports, type LabReportItem } from '@/services/doctor/labReport';
|
||||
import { listPatients } from '@/services/doctor/patient';
|
||||
import Loading from '@/components/Loading';
|
||||
import ErrorState from '@/components/ErrorState';
|
||||
import EmptyState from '@/components/EmptyState';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import './index.scss';
|
||||
@@ -17,17 +18,20 @@ export default function ReportList() {
|
||||
const [currentPatientId, setCurrentPatientId] = useState(patientId);
|
||||
const [reports, setReports] = useState<LabReportItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [total, setTotal] = useState(0);
|
||||
const mountedRef = useRef(false);
|
||||
|
||||
const loadReports = useCallback(async () => {
|
||||
if (!currentPatientId) return;
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await listLabReports(currentPatientId, { page: 1, page_size: 50 });
|
||||
setReports(res.data || []);
|
||||
setTotal(res.total || 0);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -65,6 +69,7 @@ export default function ReportList() {
|
||||
const formatDate = (d: string) => new Date(d).toLocaleDateString('zh-CN');
|
||||
|
||||
if (loading && reports.length === 0) return <Loading />;
|
||||
if (error) return <ErrorState onRetry={loadReports} />;
|
||||
|
||||
return (
|
||||
<ScrollView scrollY className={`report-page ${modeClass}`}>
|
||||
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
type ThreadResponse,
|
||||
} from '@/services/action-inbox';
|
||||
import Loading from '@/components/Loading';
|
||||
import ErrorState from '@/components/ErrorState';
|
||||
import SegmentTabs from '@/components/SegmentTabs';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import './index.scss';
|
||||
|
||||
@@ -40,6 +42,7 @@ export default function ActionInboxPage() {
|
||||
const [total, setTotal] = useState(0);
|
||||
const [_page, setPage] = useState(1);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState('');
|
||||
const [threadData, setThreadData] = useState<ThreadResponse | null>(null);
|
||||
const [showDetail, setShowDetail] = useState(false);
|
||||
@@ -50,6 +53,7 @@ export default function ActionInboxPage() {
|
||||
if (loadingRef.current) return;
|
||||
loadingRef.current = true;
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const resp = await listActionItems({
|
||||
page: pageNum,
|
||||
@@ -65,6 +69,7 @@ export default function ActionInboxPage() {
|
||||
setTotal(resp.total);
|
||||
setPage(pageNum);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -114,23 +119,11 @@ export default function ActionInboxPage() {
|
||||
|
||||
return (
|
||||
<View className={`action-inbox-page ${modeClass}`}>
|
||||
<View className="inbox-tabs">
|
||||
{STATUS_TABS.map((tab) => (
|
||||
<View
|
||||
key={tab.key}
|
||||
className={`inbox-tab ${activeTab === tab.key ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange(tab.key)}
|
||||
>
|
||||
<Text
|
||||
className={`inbox-tab-text ${activeTab === tab.key ? 'active' : ''}`}
|
||||
>
|
||||
{tab.label}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<SegmentTabs tabs={STATUS_TABS} activeKey={activeTab} onChange={handleTabChange} variant="underline" />
|
||||
|
||||
{items.length === 0 && !loading ? (
|
||||
{error ? (
|
||||
<ErrorState onRetry={() => fetchItems(1, activeTab, true)} />
|
||||
) : items.length === 0 && !loading ? (
|
||||
<View className="inbox-empty">
|
||||
<Text className="inbox-empty-text">暂无待办事项</Text>
|
||||
</View>
|
||||
|
||||
@@ -4,11 +4,13 @@ import Taro from '@tarojs/taro';
|
||||
import { usePageData } from '@/hooks/usePageData';
|
||||
import { listSessions, type ConsultationSession } from '@/services/doctor/consultation';
|
||||
import Loading from '@/components/Loading';
|
||||
import ErrorState from '@/components/ErrorState';
|
||||
import EmptyState from '@/components/EmptyState';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import { getStatusInlineStyle, getStatusLabel } from '@/utils/statusTag';
|
||||
import { formatDateTime } from '@/utils/date';
|
||||
import { safeNavigateTo } from '@/utils/navigate';
|
||||
import SegmentTabs from '@/components/SegmentTabs';
|
||||
import './index.scss';
|
||||
|
||||
const TABS = [
|
||||
@@ -23,6 +25,7 @@ export default function ConsultationList() {
|
||||
const [sessions, setSessions] = useState<ConsultationSession[]>([]);
|
||||
const [activeTab, setActiveTab] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
const mountedRef = useRef(false);
|
||||
@@ -31,6 +34,7 @@ export default function ConsultationList() {
|
||||
|
||||
const loadSessions = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await listSessions({
|
||||
page,
|
||||
@@ -40,6 +44,7 @@ export default function ConsultationList() {
|
||||
setSessions(res.data || []);
|
||||
setTotal(res.total || 0);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -67,20 +72,11 @@ export default function ConsultationList() {
|
||||
};
|
||||
|
||||
if (loading && sessions.length === 0) return <Loading />;
|
||||
if (error) return <ErrorState onRetry={loadSessions} />;
|
||||
|
||||
return (
|
||||
<ScrollView scrollY className={`consultation-page ${modeClass}`}>
|
||||
<View className='tabs'>
|
||||
{TABS.map((t) => (
|
||||
<View
|
||||
key={t.key}
|
||||
className={`tab ${activeTab === t.key ? 'tab--active' : ''}`}
|
||||
onClick={() => handleTabChange(t.key)}
|
||||
>
|
||||
<Text>{t.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<SegmentTabs tabs={TABS} activeKey={activeTab} onChange={handleTabChange} variant="underline" />
|
||||
|
||||
{sessions.length === 0 ? (
|
||||
<EmptyState text='暂无咨询会话' />
|
||||
|
||||
@@ -4,9 +4,11 @@ import Taro, { useRouter } from '@tarojs/taro';
|
||||
import { usePageData } from '@/hooks/usePageData';
|
||||
import { listFollowUpTasks, type FollowUpTask } from '@/services/doctor/followup';
|
||||
import Loading from '@/components/Loading';
|
||||
import ErrorState from '@/components/ErrorState';
|
||||
import EmptyState from '@/components/EmptyState';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import { getStatusInlineStyle, getStatusLabel } from '@/utils/statusTag';
|
||||
import SegmentTabs from '@/components/SegmentTabs';
|
||||
import './index.scss';
|
||||
|
||||
const TABS = [
|
||||
@@ -24,11 +26,13 @@ export default function FollowUpList() {
|
||||
const [tasks, setTasks] = useState<FollowUpTask[]>([]);
|
||||
const [activeTab, setActiveTab] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
const [total, setTotal] = useState(0);
|
||||
const mountedRef = useRef(false);
|
||||
|
||||
const loadTasks = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await listFollowUpTasks({
|
||||
page: 1,
|
||||
@@ -39,6 +43,7 @@ export default function FollowUpList() {
|
||||
setTasks(res.data || []);
|
||||
setTotal(res.total || 0);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -70,20 +75,11 @@ export default function FollowUpList() {
|
||||
};
|
||||
|
||||
if (loading && tasks.length === 0) return <Loading />;
|
||||
if (error) return <ErrorState onRetry={loadTasks} />;
|
||||
|
||||
return (
|
||||
<ScrollView scrollY className={`followup-page ${modeClass}`}>
|
||||
<View className='tabs'>
|
||||
{TABS.map((t) => (
|
||||
<View
|
||||
key={t.key}
|
||||
className={`tab ${activeTab === t.key ? 'tab--active' : ''}`}
|
||||
onClick={() => setActiveTab(t.key)}
|
||||
>
|
||||
<Text>{t.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<SegmentTabs tabs={TABS} activeKey={activeTab} onChange={(key) => setActiveTab(key)} variant="underline" />
|
||||
|
||||
<View className='task-count'>
|
||||
<Text>共 {total} 项任务</Text>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
min-height: 100vh;
|
||||
background: $bg;
|
||||
padding: 32px;
|
||||
padding-bottom: 120px;
|
||||
padding-bottom: calc(160px + env(safe-area-inset-bottom));
|
||||
|
||||
&__header {
|
||||
margin-bottom: 40px;
|
||||
|
||||
@@ -5,6 +5,8 @@ import { usePageData } from '@/hooks/usePageData';
|
||||
import { listPatientAlerts, type Alert } from '@/services/alert';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import Loading from '@/components/Loading';
|
||||
import ErrorState from '@/components/ErrorState';
|
||||
import SegmentTabs from '@/components/SegmentTabs';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import './index.scss';
|
||||
|
||||
@@ -30,11 +32,13 @@ export default function PatientAlerts() {
|
||||
const [page, setPage] = useState(1);
|
||||
const [status, setStatus] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const fetchAlerts = useCallback(
|
||||
async (pageNum: number, s: string, isRefresh = false) => {
|
||||
if (!currentPatient) return;
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await listPatientAlerts(currentPatient.id, {
|
||||
page: pageNum,
|
||||
@@ -50,6 +54,7 @@ export default function PatientAlerts() {
|
||||
setTotal(res.total);
|
||||
setPage(pageNum);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -74,35 +79,26 @@ export default function PatientAlerts() {
|
||||
if (!currentPatient) {
|
||||
return (
|
||||
<View className={`alerts-page ${modeClass}`}>
|
||||
<View className='alerts-empty'>
|
||||
<Text className='alerts-empty-text'>请先完善个人档案</Text>
|
||||
<View className='alerts-empty-action' onClick={() => Taro.navigateTo({ url: '/pages/pkg-profile/family-add/index' })}>
|
||||
<Text className='alerts-empty-action-text'>去建档</Text>
|
||||
</View>
|
||||
</View>
|
||||
<ErrorState text='请先完善个人档案' onRetry={() => Taro.navigateTo({ url: '/pages/pkg-profile/family-add/index' })} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<View className={`alerts-page ${modeClass}`}>
|
||||
<SegmentTabs tabs={STATUS_TABS} activeKey={status} onChange={handleTabChange} variant="pill" />
|
||||
<ErrorState onRetry={() => fetchAlerts(1, status, true)} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View className={`alerts-page ${modeClass}`}>
|
||||
<View className='alerts-tabs'>
|
||||
{STATUS_TABS.map((tab) => (
|
||||
<View
|
||||
key={tab.key}
|
||||
className={`alerts-tab ${status === tab.key ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange(tab.key)}
|
||||
>
|
||||
<Text className={`alerts-tab-text ${status === tab.key ? 'active' : ''}`}>{tab.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<SegmentTabs tabs={STATUS_TABS} activeKey={status} onChange={handleTabChange} variant="pill" />
|
||||
|
||||
{alerts.length === 0 && !loading ? (
|
||||
<View className='alerts-empty'>
|
||||
<Text className='alerts-empty-text'>暂无告警记录</Text>
|
||||
<Text className='alerts-empty-hint'>您的各项指标正常</Text>
|
||||
</View>
|
||||
<ErrorState text='暂无告警记录' hint='您的各项指标正常' />
|
||||
) : (
|
||||
<View className='alerts-list'>
|
||||
{alerts.map((item) => {
|
||||
|
||||
@@ -5,7 +5,9 @@ import { usePageData } from '@/hooks/usePageData';
|
||||
import { useHealthStore } from '@/stores/health';
|
||||
import TrendChart from '@/components/TrendChart';
|
||||
import Loading from '@/components/Loading';
|
||||
import ErrorState from '@/components/ErrorState';
|
||||
import EmptyState from '@/components/EmptyState';
|
||||
import SegmentTabs from '@/components/SegmentTabs';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import './index.scss';
|
||||
|
||||
@@ -32,14 +34,17 @@ export default function Trend() {
|
||||
const [range, setRange] = useState('7d');
|
||||
const [points, setPoints] = useState<{ date: string; value: number }[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
const getTrend = useHealthStore((s) => s.getTrend);
|
||||
|
||||
const fetchTrend = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const data = await getTrend(indicator, range);
|
||||
setPoints(data);
|
||||
} catch {
|
||||
setError(true);
|
||||
setPoints([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -72,25 +77,17 @@ export default function Trend() {
|
||||
</View>
|
||||
|
||||
{/* 时间范围切换 */}
|
||||
<View className='trange-wrap'>
|
||||
{RANGE_OPTIONS.map((opt) => (
|
||||
<View
|
||||
key={opt.value}
|
||||
className={`trange-tab ${range === opt.value ? 'trange-tab-active' : ''}`}
|
||||
onClick={() => setRange(opt.value)}
|
||||
>
|
||||
<Text className={`trange-tab-text ${range === opt.value ? 'trange-tab-text-active' : ''}`}>
|
||||
{opt.label}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<SegmentTabs tabs={RANGE_OPTIONS.map(o => ({ key: o.value, label: o.label }))} activeKey={range} onChange={setRange} variant="pill" />
|
||||
|
||||
{/* ECharts 折线图 */}
|
||||
{loading ? (
|
||||
<View className='trend-chart-card'>
|
||||
<Loading />
|
||||
</View>
|
||||
) : error ? (
|
||||
<View className='trend-chart-card'>
|
||||
<ErrorState onRetry={fetchTrend} />
|
||||
</View>
|
||||
) : points.length === 0 ? (
|
||||
<View className='trend-chart-card'>
|
||||
<EmptyState text='暂无趋势数据' />
|
||||
|
||||
@@ -5,7 +5,9 @@ import { usePageData } from '@/hooks/usePageData';
|
||||
import { listMyOrders } from '../../../services/points';
|
||||
import type { PointsOrder } from '../../../services/points';
|
||||
import EmptyState from '../../../components/EmptyState';
|
||||
import ErrorState from '../../../components/ErrorState';
|
||||
import Loading from '../../../components/Loading';
|
||||
import SegmentTabs from '../../../components/SegmentTabs';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import './index.scss';
|
||||
|
||||
@@ -30,10 +32,12 @@ export default function MallOrders() {
|
||||
const [page, setPage] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const fetchOrders = useCallback(
|
||||
async (pageNum: number, status: string, isRefresh = false) => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await listMyOrders({
|
||||
page: pageNum,
|
||||
@@ -51,6 +55,7 @@ export default function MallOrders() {
|
||||
setTotal(res.total);
|
||||
setPage(pageNum);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -108,20 +113,12 @@ export default function MallOrders() {
|
||||
return (
|
||||
<View className={`orders-page ${modeClass}`}>
|
||||
{/* 状态筛选标签 */}
|
||||
<View className='status-tabs'>
|
||||
{STATUS_TABS.map((tab) => (
|
||||
<View
|
||||
key={tab.key}
|
||||
className={`status-tab ${activeTab === tab.key ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange(tab.key)}
|
||||
>
|
||||
<Text className='status-tab-text'>{tab.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<SegmentTabs tabs={STATUS_TABS} activeKey={activeTab} onChange={handleTabChange} variant="underline" />
|
||||
|
||||
{/* 订单列表 */}
|
||||
{orders.length === 0 && !loading ? (
|
||||
{error ? (
|
||||
<ErrorState onRetry={() => fetchOrders(1, activeTab, true)} />
|
||||
) : orders.length === 0 && !loading ? (
|
||||
<EmptyState
|
||||
icon=''
|
||||
text='暂无订单'
|
||||
|
||||
@@ -4,6 +4,7 @@ import Taro from '@tarojs/taro';
|
||||
import { usePageData } from '@/hooks/usePageData';
|
||||
import { listTasks, FollowUpTask } from '../../../services/followup';
|
||||
import EmptyState from '../../../components/EmptyState';
|
||||
import ErrorState from '../../../components/ErrorState';
|
||||
import Loading from '../../../components/Loading';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import './index.scss';
|
||||
@@ -19,13 +20,16 @@ export default function MyFollowUps() {
|
||||
const [activeTab, setActiveTab] = useState('pending');
|
||||
const [tasks, setTasks] = useState<FollowUpTask[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const fetchTasks = useCallback(async (status: string) => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await listTasks(status);
|
||||
setTasks(res.data || []);
|
||||
} catch {
|
||||
setError(true);
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -70,34 +74,40 @@ export default function MyFollowUps() {
|
||||
))}
|
||||
</View>
|
||||
|
||||
<View className='task-list'>
|
||||
{tasks.map((t) => (
|
||||
<View
|
||||
className='task-card'
|
||||
key={t.id}
|
||||
onClick={() => goToDetail(t.id)}
|
||||
>
|
||||
<View className='task-top'>
|
||||
<Text className='task-name'>{t.follow_up_type}</Text>
|
||||
<Text className={`task-status ${getStatusClass(t.status)}`}>
|
||||
{getStatusLabel(t.status)}
|
||||
</Text>
|
||||
</View>
|
||||
<Text className='task-desc'>{t.content_template}</Text>
|
||||
<Text className='task-due'>截止: {t.planned_date}</Text>
|
||||
{error ? (
|
||||
<ErrorState onRetry={() => fetchTasks(activeTab)} />
|
||||
) : (
|
||||
<>
|
||||
<View className='task-list'>
|
||||
{tasks.map((t) => (
|
||||
<View
|
||||
className='task-card'
|
||||
key={t.id}
|
||||
onClick={() => goToDetail(t.id)}
|
||||
>
|
||||
<View className='task-top'>
|
||||
<Text className='task-name'>{t.follow_up_type}</Text>
|
||||
<Text className={`task-status ${getStatusClass(t.status)}`}>
|
||||
{getStatusLabel(t.status)}
|
||||
</Text>
|
||||
</View>
|
||||
<Text className='task-desc'>{t.content_template}</Text>
|
||||
<Text className='task-due'>截止: {t.planned_date}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{tasks.length === 0 && !loading && (
|
||||
<EmptyState text={`暂无${(() => {
|
||||
const tab = TABS.find((t) => t.key === activeTab);
|
||||
return tab ? tab.label : '';
|
||||
})()}任务`} />
|
||||
)}
|
||||
{tasks.length === 0 && !loading && (
|
||||
<EmptyState text={`暂无${(() => {
|
||||
const tab = TABS.find((t) => t.key === activeTab);
|
||||
return tab ? tab.label : '';
|
||||
})()}任务`} />
|
||||
)}
|
||||
|
||||
{loading && (
|
||||
<Loading />
|
||||
{loading && (
|
||||
<Loading />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user