fix(mp): Phase 3 品质打磨 — Loading优化+ErrorBoundary重试上限+登录安全+输入限制
- Loading 组件区分列表底部状态(无spinner)vs 加载中状态 - ErrorBoundary 添加 MAX_RETRIES=3 限制,超出提示重启 - login 页 IS_SIMULATOR 改为 === 'develop' 精确匹配 - login 密码输入 type 改为 safe-password 防截屏 - appointment/create 备注输入添加 maxlength=200 - GuestHome "查看全部" 导航到文章列表页
This commit is contained in:
@@ -8,20 +8,24 @@ interface Props {
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
retryCount: number;
|
||||
}
|
||||
|
||||
const MAX_RETRIES = 3;
|
||||
|
||||
export default class ErrorBoundary extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
this.state = { hasError: false, retryCount: 0 };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(): State {
|
||||
static getDerivedStateFromError(): Partial<State> {
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
||||
console.error('[ErrorBoundary]', error, info.componentStack);
|
||||
this.setState((prev) => ({ retryCount: prev.retryCount + 1 }));
|
||||
}
|
||||
|
||||
handleRetry = () => {
|
||||
@@ -30,19 +34,24 @@ export default class ErrorBoundary extends Component<Props, State> {
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
const exceeded = this.state.retryCount >= MAX_RETRIES;
|
||||
return (
|
||||
<View className='error-boundary'>
|
||||
<View className='error-icon-wrap'>
|
||||
<Text className='error-icon-text'>!</Text>
|
||||
</View>
|
||||
<Text className='error-title'>页面出了点问题</Text>
|
||||
<Text className='error-desc'>请返回重试</Text>
|
||||
<View
|
||||
className='error-retry-btn'
|
||||
onClick={this.handleRetry}
|
||||
>
|
||||
<Text className='error-retry-text'>重新加载</Text>
|
||||
</View>
|
||||
<Text className='error-desc'>
|
||||
{exceeded ? '请重启小程序' : '请返回重试'}
|
||||
</Text>
|
||||
{!exceeded && (
|
||||
<View
|
||||
className='error-retry-btn'
|
||||
onClick={this.handleRetry}
|
||||
>
|
||||
<Text className='error-retry-text'>重新加载</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,3 +28,12 @@
|
||||
font-size: var(--tk-font-body-sm);
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
|
||||
.loading-state--end {
|
||||
padding: 24px 0;
|
||||
|
||||
.loading-state-text {
|
||||
color: var(--tk-text-tertiary);
|
||||
font-size: var(--tk-caption);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ interface LoadingProps {
|
||||
}
|
||||
|
||||
export default React.memo(function Loading({ text = '加载中...' }: LoadingProps) {
|
||||
const isListEnd = text !== '加载中...' && !text.includes('加载');
|
||||
return (
|
||||
<View className='loading-state'>
|
||||
<View className='loading-spinner' />
|
||||
<View className={`loading-state ${isListEnd ? 'loading-state--end' : ''}`}>
|
||||
{!isListEnd && <View className='loading-spinner' />}
|
||||
<Text className='loading-state-text'>{text}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user