Files
kanban/components/TaskCard.vue
xiaomai 485d75820b feat(ui): overhaul interface with Nuxt UI
Integrate the Nuxt UI component library and completely revamp the application's user interface to improve usability, aesthetics, and maintainability.

- Replace all custom components and native browser dialogs (`alert`, `prompt`, `confirm`) with Nuxt UI components like `UCard`, `UButton`, `UModal`, and `UNotifications`.
- Refactor the main page by extracting the sidebar into dedicated panel components: `CategoryPanel`, `BoardSummaryPanel`, and `HistoryPanel`.
- Redesign all major components (Toolbar, Board, Stage, Task) for a cleaner layout and improved information hierarchy.
- Implement user-friendly modals for all creation, editing, and deletion flows, providing a more consistent user experience.
- Add toast notifications to provide immediate feedback for user actions.
2025-10-22 17:52:17 +08:00

92 lines
2.9 KiB
Vue

<template>
<UCard
class="group cursor-grab select-none border border-slate-800 bg-slate-900/80 transition hover:border-sky-500/40"
data-task
:data-id="taskId"
draggable="true"
@dragstart="onDragStart"
>
<div class="flex items-start gap-3">
<div class="flex h-6 w-6 items-center justify-center rounded-md bg-slate-800/80 text-xs text-slate-400">
<UIcon name="i-heroicons-arrows-up-down-20-solid" class="h-4 w-4" />
</div>
<div class="min-w-0 flex-1 space-y-2">
<div class="flex flex-wrap items-center gap-2">
<h4 class="truncate font-semibold text-slate-100">{{ task?.title || '未命名任务' }}</h4>
<UBadge size="xs" color="neutral" variant="subtle">{{ stat }}</UBadge>
</div>
<div v-if="hasSteps" class="space-y-1">
<UProgress :value="progress" color="primary" class="h-1.5" />
<p class="text-[11px] text-slate-400">步骤完成度 {{ progress }}%</p>
</div>
<p v-if="task?.description" class="line-clamp-2 text-xs text-slate-400">
{{ task.description }}
</p>
</div>
<div class="flex flex-col items-end gap-2">
<UBadge
size="xs"
variant="soft"
:style="{
backgroundColor: `${categoryColor}1A`,
color: categoryColor,
borderColor: `${categoryColor}33`
}"
>
{{ categoryTitle }}
</UBadge>
<UButton
size="2xs"
color="primary"
variant="soft"
icon="i-heroicons-pencil-square-20-solid"
@click.stop="modalOpen = true"
>
详情
</UButton>
</div>
</div>
</UCard>
<TaskModal v-model:open="modalOpen" :task-id="taskId" />
</template>
<script setup lang="ts">
import { useBoardStore } from '~/stores/board'
import TaskModal from './TaskModal.vue'
const store = useBoardStore()
const props = defineProps<{ taskId: string }>()
const task = computed(() => store.taskById(props.taskId))
const category = computed(() => {
const id = task.value?.category
return id ? store.categoryById(id) : null
})
const categoryTitle = computed(() => category.value?.title || '未分类')
const categoryColor = computed(() => `#${(category.value?.color || '64748b').padStart(6, '0')}`)
const steps = computed(() => task.value?.steps || [])
const hasSteps = computed(() => steps.value.length > 0)
const stat = computed(() => {
const done = steps.value.filter((s) => s.done).length
return `${done}/${steps.value.length}`
})
const progress = computed(() => {
if (!steps.value.length) return 0
const done = steps.value.filter((s) => s.done).length
return Math.round((done / steps.value.length) * 100)
})
const modalOpen = ref(false)
function onDragStart(e: DragEvent) {
e.dataTransfer?.setData('text/task', props.taskId)
e.dataTransfer?.setDragImage(new Image(), 0, 0)
e.dataTransfer!.effectAllowed = 'move'
}
</script>