feat(miniprogram): 通用组件 + 页面接入 — Chunk 7
- 创建 EmptyState/ErrorState/Loading 三个通用组件 - 8个列表页面接入通用组件替换内联空状态/loading - app.config.ts 添加 login 页面路由
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
export default defineAppConfig({
|
export default defineAppConfig({
|
||||||
pages: [
|
pages: [
|
||||||
'pages/index/index',
|
'pages/index/index',
|
||||||
|
'pages/login/index',
|
||||||
'pages/health/index',
|
'pages/health/index',
|
||||||
'pages/health/input/index',
|
'pages/health/input/index',
|
||||||
'pages/health/trend/index',
|
'pages/health/trend/index',
|
||||||
@@ -20,7 +21,6 @@ export default defineAppConfig({
|
|||||||
'pages/profile/followups/index',
|
'pages/profile/followups/index',
|
||||||
'pages/profile/medication/index',
|
'pages/profile/medication/index',
|
||||||
'pages/profile/settings/index',
|
'pages/profile/settings/index',
|
||||||
'pages/login/index',
|
|
||||||
],
|
],
|
||||||
tabBar: {
|
tabBar: {
|
||||||
color: '#94A3B8',
|
color: '#94A3B8',
|
||||||
|
|||||||
37
apps/miniprogram/src/components/EmptyState/index.scss
Normal file
37
apps/miniprogram/src/components/EmptyState/index.scss
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 120px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state-icon {
|
||||||
|
font-size: 80px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state-text {
|
||||||
|
font-size: 30px;
|
||||||
|
color: $tx2;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state-hint {
|
||||||
|
font-size: 24px;
|
||||||
|
color: $tx3;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state-action {
|
||||||
|
background: $pri;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 16px 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state-action-text {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
32
apps/miniprogram/src/components/EmptyState/index.tsx
Normal file
32
apps/miniprogram/src/components/EmptyState/index.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from '@tarojs/components';
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
interface EmptyStateProps {
|
||||||
|
icon?: string;
|
||||||
|
text: string;
|
||||||
|
hint?: string;
|
||||||
|
actionText?: string;
|
||||||
|
onAction?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EmptyState({
|
||||||
|
icon = '📭',
|
||||||
|
text,
|
||||||
|
hint,
|
||||||
|
actionText,
|
||||||
|
onAction,
|
||||||
|
}: EmptyStateProps) {
|
||||||
|
return (
|
||||||
|
<View className='empty-state'>
|
||||||
|
<Text className='empty-state-icon'>{icon}</Text>
|
||||||
|
<Text className='empty-state-text'>{text}</Text>
|
||||||
|
{hint && <Text className='empty-state-hint'>{hint}</Text>}
|
||||||
|
{actionText && onAction && (
|
||||||
|
<View className='empty-state-action' onClick={onAction}>
|
||||||
|
<Text className='empty-state-action-text'>{actionText}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
32
apps/miniprogram/src/components/ErrorState/index.scss
Normal file
32
apps/miniprogram/src/components/ErrorState/index.scss
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
|
.error-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 120px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-state-icon {
|
||||||
|
font-size: 80px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-state-text {
|
||||||
|
font-size: 28px;
|
||||||
|
color: $tx2;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-state-retry {
|
||||||
|
background: $pri;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 16px 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-state-retry-text {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
25
apps/miniprogram/src/components/ErrorState/index.tsx
Normal file
25
apps/miniprogram/src/components/ErrorState/index.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from '@tarojs/components';
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
interface ErrorStateProps {
|
||||||
|
text?: string;
|
||||||
|
onRetry?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ErrorState({
|
||||||
|
text = '加载失败,请稍后重试',
|
||||||
|
onRetry,
|
||||||
|
}: ErrorStateProps) {
|
||||||
|
return (
|
||||||
|
<View className='error-state'>
|
||||||
|
<Text className='error-state-icon'>⚠️</Text>
|
||||||
|
<Text className='error-state-text'>{text}</Text>
|
||||||
|
{onRetry && (
|
||||||
|
<View className='error-state-retry' onClick={onRetry}>
|
||||||
|
<Text className='error-state-retry-text'>重新加载</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
30
apps/miniprogram/src/components/Loading/index.scss
Normal file
30
apps/miniprogram/src/components/Loading/index.scss
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
|
.loading-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 80px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border: 4px solid $bd;
|
||||||
|
border-top-color: $pri;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-state-text {
|
||||||
|
font-size: 26px;
|
||||||
|
color: $tx3;
|
||||||
|
}
|
||||||
16
apps/miniprogram/src/components/Loading/index.tsx
Normal file
16
apps/miniprogram/src/components/Loading/index.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from '@tarojs/components';
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
interface LoadingProps {
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Loading({ text = '加载中...' }: LoadingProps) {
|
||||||
|
return (
|
||||||
|
<View className='loading-state'>
|
||||||
|
<View className='loading-spinner' />
|
||||||
|
<Text className='loading-state-text'>{text}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ import { View, Text } from '@tarojs/components';
|
|||||||
import Taro, { useDidShow, useReachBottom, usePullDownRefresh } from '@tarojs/taro';
|
import Taro, { useDidShow, useReachBottom, usePullDownRefresh } from '@tarojs/taro';
|
||||||
import { listAppointments, cancelAppointment } from '../../services/appointment';
|
import { listAppointments, cancelAppointment } from '../../services/appointment';
|
||||||
import type { Appointment } from '../../services/appointment';
|
import type { Appointment } from '../../services/appointment';
|
||||||
|
import EmptyState from '../../components/EmptyState';
|
||||||
|
import Loading from '../../components/Loading';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const STATUS_MAP: Record<string, { label: string; className: string }> = {
|
const STATUS_MAP: Record<string, { label: string; className: string }> = {
|
||||||
@@ -82,11 +84,7 @@ export default function AppointmentList() {
|
|||||||
|
|
||||||
{/* 预约列表 */}
|
{/* 预约列表 */}
|
||||||
{appointments.length === 0 && !loading ? (
|
{appointments.length === 0 && !loading ? (
|
||||||
<View className='empty-state'>
|
<EmptyState icon='📋' text='暂无预约记录' hint='点击下方按钮新建预约' />
|
||||||
<Text className='empty-icon'>📋</Text>
|
|
||||||
<Text className='empty-text'>暂无预约记录</Text>
|
|
||||||
<Text className='empty-hint'>点击下方按钮新建预约</Text>
|
|
||||||
</View>
|
|
||||||
) : (
|
) : (
|
||||||
<View className='appointment-list'>
|
<View className='appointment-list'>
|
||||||
{appointments.map((item) => {
|
{appointments.map((item) => {
|
||||||
@@ -120,14 +118,10 @@ export default function AppointmentList() {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{loading && (
|
{loading && (
|
||||||
<View className='loading-tip'>
|
<Loading />
|
||||||
<Text className='loading-text'>加载中...</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
{!loading && appointments.length >= total && total > 0 && (
|
{!loading && appointments.length >= total && total > 0 && (
|
||||||
<View className='loading-tip'>
|
<Loading text='没有更多了' />
|
||||||
<Text className='loading-text'>没有更多了</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useDidShow, usePullDownRefresh, useReachBottom } from '@tarojs/taro';
|
import Taro, { useDidShow, usePullDownRefresh, useReachBottom } from '@tarojs/taro';
|
||||||
import { listArticles, Article } from '../../services/article';
|
import { listArticles, Article } from '../../services/article';
|
||||||
|
import EmptyState from '../../components/EmptyState';
|
||||||
|
import Loading from '../../components/Loading';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
export default function ArticleList() {
|
export default function ArticleList() {
|
||||||
@@ -80,15 +82,11 @@ export default function ArticleList() {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{articles.length === 0 && !loading && (
|
{articles.length === 0 && !loading && (
|
||||||
<View className='empty-state'>
|
<EmptyState text='暂无资讯文章' />
|
||||||
<Text className='empty-text'>暂无资讯文章</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
<View className='loading-hint'>
|
<Loading />
|
||||||
<Text className='loading-text'>加载中...</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useDidShow } from '@tarojs/taro';
|
import Taro, { useDidShow } from '@tarojs/taro';
|
||||||
import { listTasks, FollowUpTask } from '../../services/followup';
|
import { listTasks, FollowUpTask } from '../../services/followup';
|
||||||
|
import EmptyState from '../../components/EmptyState';
|
||||||
|
import Loading from '../../components/Loading';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const TABS = [
|
const TABS = [
|
||||||
@@ -89,18 +91,14 @@ export default function FollowUpList() {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{tasks.length === 0 && !loading && (
|
{tasks.length === 0 && !loading && (
|
||||||
<View className='empty-state'>
|
<EmptyState text={`暂无${(() => {
|
||||||
<Text className='empty-text'>暂无{(() => {
|
const tab = TABS.find((t) => t.key === activeTab);
|
||||||
const tab = TABS.find((t) => t.key === activeTab);
|
return tab ? tab.label : '';
|
||||||
return tab ? tab.label : '';
|
})()}任务`} />
|
||||||
})()}任务</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
<View className='loading-hint'>
|
<Loading />
|
||||||
<Text className='loading-text'>加载中...</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useDidShow } from '@tarojs/taro';
|
import Taro, { useDidShow } from '@tarojs/taro';
|
||||||
import { useAuthStore } from '../../stores/auth';
|
import { useAuthStore } from '../../stores/auth';
|
||||||
|
import EmptyState from '../../components/EmptyState';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
@@ -76,9 +77,7 @@ export default function Index() {
|
|||||||
{/* 即将到来 */}
|
{/* 即将到来 */}
|
||||||
<View className='upcoming'>
|
<View className='upcoming'>
|
||||||
<Text className='section-title'>即将到来</Text>
|
<Text className='section-title'>即将到来</Text>
|
||||||
<View className='empty-hint'>
|
<EmptyState text='暂无即将到来的预约' />
|
||||||
<Text className='empty-text'>暂无即将到来的预约</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { View, Text } from '@tarojs/components';
|
|||||||
import Taro, { useDidShow } from '@tarojs/taro';
|
import Taro, { useDidShow } from '@tarojs/taro';
|
||||||
import { listPatients, Patient } from '../../../services/patient';
|
import { listPatients, Patient } from '../../../services/patient';
|
||||||
import { useAuthStore } from '../../../stores/auth';
|
import { useAuthStore } from '../../../stores/auth';
|
||||||
|
import EmptyState from '../../../components/EmptyState';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
export default function FamilyList() {
|
export default function FamilyList() {
|
||||||
@@ -72,9 +73,7 @@ export default function FamilyList() {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{patients.length === 0 && !loading && (
|
{patients.length === 0 && !loading && (
|
||||||
<View className='empty-state'>
|
<EmptyState text='暂无就诊人' />
|
||||||
<Text className='empty-text'>暂无就诊人</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<View className='family-add-btn' onClick={goToAdd}>
|
<View className='family-add-btn' onClick={goToAdd}>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useDidShow, usePullDownRefresh, useReachBottom } from '@tarojs/taro';
|
import Taro, { useDidShow, usePullDownRefresh, useReachBottom } from '@tarojs/taro';
|
||||||
import { listReports, LabReport } from '../../../services/report';
|
import { listReports, LabReport } from '../../../services/report';
|
||||||
|
import EmptyState from '../../../components/EmptyState';
|
||||||
|
import Loading from '../../../components/Loading';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const PAGE_SIZE = 20;
|
const PAGE_SIZE = 20;
|
||||||
@@ -79,15 +81,11 @@ export default function MyReports() {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{reports.length === 0 && !loading && (
|
{reports.length === 0 && !loading && (
|
||||||
<View className='empty-state'>
|
<EmptyState text='暂无报告记录' />
|
||||||
<Text className='empty-text'>暂无报告记录</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
<View className='loading-hint'>
|
<Loading />
|
||||||
<Text className='loading-text'>加载中...</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useDidShow, usePullDownRefresh, useReachBottom } from '@tarojs/taro';
|
import Taro, { useDidShow, usePullDownRefresh, useReachBottom } from '@tarojs/taro';
|
||||||
import { listReports, LabReport } from '../../services/report';
|
import { listReports, LabReport } from '../../services/report';
|
||||||
|
import EmptyState from '../../components/EmptyState';
|
||||||
|
import Loading from '../../components/Loading';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const PAGE_SIZE = 20;
|
const PAGE_SIZE = 20;
|
||||||
@@ -82,15 +84,11 @@ export default function ReportList() {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{reports.length === 0 && !loading && (
|
{reports.length === 0 && !loading && (
|
||||||
<View className='empty-state'>
|
<EmptyState text='暂无报告记录' />
|
||||||
<Text className='empty-text'>暂无报告记录</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
<View className='loading-hint'>
|
<Loading />
|
||||||
<Text className='loading-text'>加载中...</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user