Your Name f166c04422 Add frontend component library and UI development tasks
- Create Tailwind CSS configuration with design tokens from UIDesignSpec
- Create globals.css with CSS variables and component styles
- Add React component library:
  - UI components: Button, Card, Tag, Input, Select, ProgressBar, Modal
  - Navigation: BottomNav, Sidebar, StatusBar
  - Layout: MobileLayout, DesktopLayout
- Add constants for colors, icons, and layout
- Update tasks.md with 31 UI development tasks linked to design node IDs
- Configure package.json, tsconfig.json, and postcss.config.js

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 17:44:22 +08:00

130 lines
3.3 KiB
TypeScript

/**
* ProgressBar 进度条组件
* 用于审核进度展示
*/
import React from 'react';
export interface ProgressBarProps {
value: number; // 0-100
max?: number;
showLabel?: boolean;
size?: 'sm' | 'md' | 'lg';
variant?: 'default' | 'success' | 'warning' | 'error';
className?: string;
}
const sizeStyles = {
sm: 'h-1',
md: 'h-2',
lg: 'h-3',
};
const variantStyles = {
default: 'bg-accent-indigo',
success: 'bg-accent-green',
warning: 'bg-accent-amber',
error: 'bg-accent-coral',
};
export const ProgressBar: React.FC<ProgressBarProps> = ({
value,
max = 100,
showLabel = false,
size = 'md',
variant = 'default',
className = '',
}) => {
const percentage = Math.min(100, Math.max(0, (value / max) * 100));
return (
<div className={`w-full ${className}`}>
{showLabel && (
<div className="flex justify-between mb-1">
<span className="text-small text-text-secondary"></span>
<span className="text-small text-text-primary">{Math.round(percentage)}%</span>
</div>
)}
<div className={`w-full bg-bg-elevated rounded-full overflow-hidden ${sizeStyles[size]}`}>
<div
className={`h-full rounded-full transition-all duration-300 ${variantStyles[variant]}`}
style={{ width: `${percentage}%` }}
/>
</div>
</div>
);
};
// 环形进度条 (用于审核中状态)
export interface CircularProgressProps {
value: number; // 0-100
size?: number;
strokeWidth?: number;
variant?: 'default' | 'success' | 'warning' | 'error';
showLabel?: boolean;
label?: string;
className?: string;
}
const circularVariantColors = {
default: '#6366F1',
success: '#32D583',
warning: '#F59E0B',
error: '#E85A4F',
};
export const CircularProgress: React.FC<CircularProgressProps> = ({
value,
size = 120,
strokeWidth = 8,
variant = 'default',
showLabel = true,
label,
className = '',
}) => {
const percentage = Math.min(100, Math.max(0, value));
const radius = (size - strokeWidth) / 2;
const circumference = radius * 2 * Math.PI;
const offset = circumference - (percentage / 100) * circumference;
return (
<div className={`relative inline-flex items-center justify-center ${className}`}>
<svg width={size} height={size} className="-rotate-90">
{/* Background circle */}
<circle
cx={size / 2}
cy={size / 2}
r={radius}
fill="none"
stroke="#27272A"
strokeWidth={strokeWidth}
/>
{/* Progress circle */}
<circle
cx={size / 2}
cy={size / 2}
r={radius}
fill="none"
stroke={circularVariantColors[variant]}
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeDasharray={circumference}
strokeDashoffset={offset}
className="transition-all duration-500"
/>
</svg>
{showLabel && (
<div className="absolute inset-0 flex flex-col items-center justify-center">
<span className="text-card-title text-text-primary">
{Math.round(percentage)}%
</span>
{label && (
<span className="text-small text-text-tertiary mt-1">{label}</span>
)}
</div>
)}
</div>
);
};
export default ProgressBar;