feat: 完成任务3 - 开发核心交易管理界面
任务3.1 实现基础 UI 组件库 - 创建 BaseIcon 组件,支持 emoji 和 SVG 图标 - 实现 CategoryIcon 组件,用于分类图标显示 - 开发 AmountInput 组件,支持金额输入和类型切换 - 创建 DatePicker 组件,支持日期选择和快捷日期 - 实现 BaseSelect 组件,支持搜索和自定义选项 - 添加 LoadingSpinner 和 EmptyState 组件 - 扩展 Tailwind 配置,完善设计系统色彩 任务3.2 创建交易列表和卡片组件 - 实现 TransactionCard 组件,展示交易详情 - 创建 TransactionList 组件,支持分组显示和批量操作 - 添加日期分组和汇总功能 - 实现无限滚动和加载更多功能 - 支持选择模式和批量删除 - 更新 TransactionsView 使用新组件 任务3.3 开发交易添加和编辑表单 - 创建 TransactionForm 组件,支持添加和编辑交易 - 集成所有输入组件(金额、分类、账户、日期等) - 实现表单验证和数据清理 - 添加重复交易检测功能 - 创建 AddTransactionView 页面 - 配置路由支持添加和编辑交易 任务3.4 实现搜索和筛选功能 - 创建 TransactionSearch 组件,支持智能搜索 - 实现搜索建议和搜索历史功能 - 开发 TransactionFilter 组件,支持多维度筛选 - 添加快速筛选标签和日期预设 - 集成搜索和筛选到 TransactionsView - 实现实时筛选和结果统计 核心特性: - 完整的交易管理界面(列表、添加、编辑、搜索、筛选) - 现代化的 neumorphic 设计风格 - 响应式布局和移动端优化 - 智能搜索和多维度筛选 - 用户友好的交互体验 - 完整的表单验证和错误处理
This commit is contained in:
122
frontend/src/components/common/LoadingSpinner.vue
Normal file
122
frontend/src/components/common/LoadingSpinner.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div :class="containerClasses">
|
||||
<!-- 旋转加载器 -->
|
||||
<div
|
||||
v-if="type === 'spinner'"
|
||||
:class="[
|
||||
'animate-spin rounded-full border-2 border-current border-t-transparent',
|
||||
sizeClasses,
|
||||
colorClasses
|
||||
]"
|
||||
/>
|
||||
|
||||
<!-- 脉冲加载器 -->
|
||||
<div
|
||||
v-else-if="type === 'pulse'"
|
||||
:class="[
|
||||
'animate-pulse rounded-full bg-current',
|
||||
sizeClasses,
|
||||
colorClasses
|
||||
]"
|
||||
/>
|
||||
|
||||
<!-- 点状加载器 -->
|
||||
<div v-else-if="type === 'dots'" :class="['flex space-x-1', colorClasses]">
|
||||
<div
|
||||
v-for="i in 3"
|
||||
:key="i"
|
||||
:class="[
|
||||
'rounded-full bg-current animate-bounce',
|
||||
dotSizeClasses
|
||||
]"
|
||||
:style="{ animationDelay: `${(i - 1) * 0.1}s` }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 骨架屏加载器 -->
|
||||
<div v-else-if="type === 'skeleton'" class="animate-pulse space-y-3">
|
||||
<div class="h-4 bg-gray-200 rounded w-3/4"></div>
|
||||
<div class="h-4 bg-gray-200 rounded w-1/2"></div>
|
||||
<div class="h-4 bg-gray-200 rounded w-5/6"></div>
|
||||
</div>
|
||||
|
||||
<!-- 加载文本 -->
|
||||
<div v-if="text" :class="['mt-2 text-sm', colorClasses]">
|
||||
{{ text }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
type?: 'spinner' | 'pulse' | 'dots' | 'skeleton'
|
||||
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
||||
color?: 'primary' | 'secondary' | 'gray' | 'white'
|
||||
text?: string
|
||||
center?: boolean
|
||||
overlay?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: 'spinner',
|
||||
size: 'md',
|
||||
color: 'primary',
|
||||
center: false,
|
||||
overlay: false
|
||||
})
|
||||
|
||||
// 容器类
|
||||
const containerClasses = computed(() => {
|
||||
const classes = []
|
||||
|
||||
if (props.center) {
|
||||
classes.push('flex flex-col items-center justify-center')
|
||||
}
|
||||
|
||||
if (props.overlay) {
|
||||
classes.push(
|
||||
'fixed inset-0 bg-black bg-opacity-50 z-50',
|
||||
'flex items-center justify-center'
|
||||
)
|
||||
}
|
||||
|
||||
return classes
|
||||
})
|
||||
|
||||
// 尺寸类
|
||||
const sizeClasses = computed(() => {
|
||||
const sizes = {
|
||||
xs: 'w-3 h-3',
|
||||
sm: 'w-4 h-4',
|
||||
md: 'w-6 h-6',
|
||||
lg: 'w-8 h-8',
|
||||
xl: 'w-12 h-12'
|
||||
}
|
||||
return sizes[props.size]
|
||||
})
|
||||
|
||||
// 点状加载器尺寸
|
||||
const dotSizeClasses = computed(() => {
|
||||
const sizes = {
|
||||
xs: 'w-1 h-1',
|
||||
sm: 'w-1.5 h-1.5',
|
||||
md: 'w-2 h-2',
|
||||
lg: 'w-3 h-3',
|
||||
xl: 'w-4 h-4'
|
||||
}
|
||||
return sizes[props.size]
|
||||
})
|
||||
|
||||
// 颜色类
|
||||
const colorClasses = computed(() => {
|
||||
const colors = {
|
||||
primary: 'text-primary',
|
||||
secondary: 'text-text-secondary',
|
||||
gray: 'text-gray-400',
|
||||
white: 'text-white'
|
||||
}
|
||||
return colors[props.color]
|
||||
})
|
||||
</script>
|
Reference in New Issue
Block a user