Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 83 additions & 7 deletions frontend/README_dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,58 @@
src/
├── components/
│ ├── ui/ # shadcn/ui基础组件
│ │ ├── Avatar.vue
│ │ ├── Button.vue
│ │ ├── Card.vue
│ │ ├── CardContent.vue
│ │ ├── CardHeader.vue
│ │ ├── DropdownMenu.vue
│ │ └── DropdownMenuItem.vue
│ ├── layout/ # 布局组件
│ │ └── Navbar.vue
│ └── features/ # 功能组件
│ ├── BoardCard.vue # 看板卡片组件
│ ├── CreateBoardDialog.vue # 创建看板对话框
│ ├── KanbanColumn.vue # 看板列组件
│ └── TaskCard.vue # 任务卡片组件
├── views/ # 页面组件
│ ├── HomeView.vue # 首页(独立页面,唯一)
│ ├── dashboard/ # 仪表板相关页面
│ ├── user/ # 用户相关页面
│ └── auth/ # 认证相关页面
│ │ ├── BoardListView.vue # 看板列表页面
│ │ └── KanbanView.vue # 看板详情页面
│ └── user/ # 用户相关页面
│ ├── ProfileView.vue # 用户资料页面
│ └── SettingsView.vue # 用户设置页面
├── stores/ # Pinia状态管理
├── composables/ # 可复用逻辑
└── lib/ # 工具函数
│ ├── board.ts # 看板状态管理
│ ├── kanban.ts # 看板任务状态管理
│ ├── user.ts # 用户状态管理
│ └── index.ts # Store统一导出
├── services/ # API服务层
│ ├── base-api.ts # 基础API服务类
│ ├── board-api.ts # 看板API服务
│ └── index.ts # 服务统一导出
├── config/ # 配置文件
│ └── api.ts # API配置
├── router/ # 路由配置
│ └── index.ts
├── lib/ # 工具函数
│ └── utils.ts
└── assets/ # 静态资源
└── vue.svg
```

#### 组件命名规范
- UI组件:`Button.vue`, `Card.vue`
- 功能组件:`TaskCard.vue`, `KanbanColumn.vue`
- 页面组件:`HomeView.vue`, `ProfileView.vue`
- **UI组件**:`Button.vue`, `Card.vue`, `Avatar.vue` - 基础UI组件,可复用
- **功能组件**:`BoardCard.vue`, `TaskCard.vue`, `KanbanColumn.vue`, `CreateBoardDialog.vue` - 业务功能组件
- **布局组件**:`Navbar.vue` - 页面布局相关组件
- **页面组件**:`HomeView.vue`, `BoardListView.vue`, `KanbanView.vue`, `ProfileView.vue` - 路由页面组件

#### 新增组件说明
- **BoardCard.vue**: 看板卡片组件,支持卡片/列表两种显示模式
- **CreateBoardDialog.vue**: 创建看板对话框,包含颜色选择器功能
- **KanbanColumn.vue**: 看板列组件,用于显示任务列
- **TaskCard.vue**: 任务卡片组件,显示单个任务信息

### 🔄 状态管理

Expand All @@ -61,6 +97,46 @@ src/
- 状态更新通过actions,避免直接修改state
- 复杂状态考虑使用computed

#### 当前Store结构
- **board.ts**: 看板状态管理,包含看板列表、创建、更新、删除等功能
- **kanban.ts**: 看板任务状态管理,包含任务CRUD操作
- **user.ts**: 用户状态管理,用户信息和设置
- **index.ts**: Store统一导出文件

### 🌐 API服务层

#### 服务架构
- **base-api.ts**: 基础API服务类,提供通用的HTTP请求方法
- **board-api.ts**: 看板相关API服务,继承自BaseApiService
- **index.ts**: 服务统一导出文件

#### API配置
- **config/api.ts**: API配置文件,包含端点配置和HTTP方法
- 支持同时定义请求路径和HTTP方法
- 提供工具函数构建API URL

#### 使用示例
```typescript
// 在组件中使用API服务
import { boardApiService } from '@/services/board-api'

const response = await boardApiService.getKanbanList()
```

### 🛣️ 路由配置

#### 当前路由结构
- **/** - HomeView.vue (首页)
- **/boards** - BoardListView.vue (看板列表页面)
- **/kanban/:boardId?** - KanbanView.vue (看板详情页面,支持可选参数)
- **/profile** - ProfileView.vue (用户资料页面)
- **/settings** - SettingsView.vue (用户设置页面)

#### 路由特点
- 使用动态路由参数支持看板ID
- 支持可选参数,提供向后兼容性
- 采用懒加载方式导入组件

#### 示例Store结构
```typescript
export const useFeatureStore = defineStore('feature', () => {
Expand Down
161 changes: 161 additions & 0 deletions frontend/src/components/features/BoardCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<template>
<!-- 卡片样式 -->
<Card
v-if="viewMode === 'card'"
class="cursor-pointer hover:shadow-lg transition-shadow duration-200"
@click="handleClick"
>
<CardHeader>
<div class="flex items-center justify-between">
<div class="flex items-center space-x-3">
<div
class="w-4 h-4 rounded-full"
:style="{ backgroundColor: board.color }"
></div>
<h3 class="text-lg font-semibold text-foreground">{{ board.name }}</h3>
</div>
<div @click.stop>
<DropdownMenu>
<template #trigger>
<Button
variant="ghost"
size="sm"
class="h-8 w-8 p-0"
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
</svg>
</Button>
</template>
<DropdownMenuItem @click="handleEdit">
编辑看板
</DropdownMenuItem>
<DropdownMenuItem @click="handleDelete" class="text-red-600">
删除看板
</DropdownMenuItem>
</DropdownMenu>
</div>
</div>
</CardHeader>
<CardContent>
<p class="text-sm text-muted-foreground mb-4">{{ board.description || '暂无描述' }}</p>
<div class="flex items-center justify-between text-xs text-muted-foreground">
<span>创建于 {{ formatDate(board.createdAt) }}</span>
<span>更新于 {{ formatDate(board.updatedAt) }}</span>
</div>
</CardContent>
</Card>

<!-- 列表样式 -->
<div
v-else
class="cursor-pointer hover:bg-muted/50 transition-colors duration-200 border-b border-border py-4 px-4 flex items-center justify-between"
@click="handleClick"
>
<div class="flex items-center space-x-4 flex-1">
<div
class="w-3 h-3 rounded-full flex-shrink-0"
:style="{ backgroundColor: board.color }"
></div>
<div class="flex-1 min-w-0">
<h3 class="text-base font-semibold text-foreground truncate">{{ board.name }}</h3>
<p class="text-sm text-muted-foreground truncate mt-1">{{ board.description || '暂无描述' }}</p>
</div>
</div>
<div class="flex items-center space-x-4 text-xs text-muted-foreground flex-shrink-0">
<span>创建于 {{ formatDate(board.createdAt) }}</span>
<span>更新于 {{ formatDate(board.updatedAt) }}</span>
<div @click.stop>
<DropdownMenu>
<template #trigger>
<Button
variant="ghost"
size="sm"
class="h-8 w-8 p-0"
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
</svg>
</Button>
</template>
<DropdownMenuItem @click="handleEdit">
编辑看板
</DropdownMenuItem>
<DropdownMenuItem @click="handleDelete" class="text-red-600">
删除看板
</DropdownMenuItem>
</DropdownMenu>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { defineProps, defineEmits } from 'vue'
import type { Board } from '@/stores/board'
import Card from '@/components/ui/Card.vue'
import CardHeader from '@/components/ui/CardHeader.vue'
import CardContent from '@/components/ui/CardContent.vue'
import Button from '@/components/ui/Button.vue'
import DropdownMenu from '@/components/ui/DropdownMenu.vue'
import DropdownMenuItem from '@/components/ui/DropdownMenuItem.vue'

/**
* 看板卡片组件
* 支持卡片和列表两种显示模式
* 提供看板的基本信息展示和操作功能
*/

interface Props {
board: Board
viewMode?: 'card' | 'list' // 显示模式:卡片或列表
}

const props = withDefaults(defineProps<Props>(), {
viewMode: 'card'
})

const emit = defineEmits<{
click: [board: Board] // 点击看板事件
edit: [board: Board] // 编辑看板事件
delete: [board: Board] // 删除看板事件
}>()

/**
* 处理看板点击事件
* 触发父组件的点击回调
*/
const handleClick = () => {
emit('click', props.board)
}

/**
* 处理编辑看板事件
* 触发父组件的编辑回调
*/
const handleEdit = () => {
emit('edit', props.board)
}

/**
* 处理删除看板事件
* 触发父组件的删除回调
*/
const handleDelete = () => {
emit('delete', props.board)
}

/**
* 格式化日期显示
* 使用中文本地化格式,显示年月日
* @param date 要格式化的日期对象
* @returns 格式化后的日期字符串
*/
const formatDate = (date: Date) => {
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'short',
day: 'numeric'
}).format(date)
}
</script>
Loading