feat(miniprogram): 通用组件 + 页面接入 — Chunk 7
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

- 创建 EmptyState/ErrorState/Loading 三个通用组件
- 8个列表页面接入通用组件替换内联空状态/loading
- app.config.ts 添加 login 页面路由
This commit is contained in:
iven
2026-04-24 01:03:23 +08:00
parent 9ef65b9a9f
commit 0c73927450
14 changed files with 201 additions and 45 deletions

View 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;
}

View 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>
);
}

View 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;
}

View 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>
);
}

View 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;
}

View 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>
);
}