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.
92 lines
2.9 KiB
Vue
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>
|