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.
This commit is contained in:
xiaomai
2025-10-22 17:52:17 +08:00
parent 2384e42933
commit 485d75820b
19 changed files with 1823 additions and 318 deletions

View File

@@ -1,44 +1,95 @@
<template>
<div class="p-3 overflow-auto grid gap-3" :style="gridStyle">
<div v-for="(col, colIndex) in columns" :key="colIndex" class="flex flex-col gap-3 min-w-[320px]">
<StageColumn v-for="sid in col" :key="sid" :stage-id="sid" :query="query" :category="category" />
<UCard class="flex h-full flex-col">
<template #header>
<div class="flex flex-wrap items-center gap-3">
<div class="flex flex-col">
<span class="text-sm font-medium text-slate-200">看板阶段</span>
<span class="text-xs text-slate-400">
{{ totalStages }} 个阶段分布在 {{ columns.length }}
</span>
</div>
<div class="flex-1" />
<UTooltip text="新增阶段">
<UButton color="primary" icon="i-heroicons-plus-circle-20-solid" @click="openStageModal()">
添加阶段
</UButton>
</UTooltip>
<UTooltip text="新增列(用于分组阶段)">
<UButton color="neutral" variant="soft" icon="i-heroicons-view-columns-20-solid" @click="addColumn">
添加列
</UButton>
</UTooltip>
</div>
</template>
<div class="relative flex-1">
<div class="flex h-full gap-4 overflow-x-auto pb-6 pr-2">
<div
v-for="(col, colIndex) in columns"
:key="`column-${colIndex}`"
class="flex min-w-[320px] flex-col gap-4"
>
<div class="flex items-center justify-between text-xs uppercase tracking-wide text-slate-400">
<span> {{ colIndex + 1 }}</span>
<UBadge color="neutral" variant="soft" :label="`${col.length} 个阶段`" />
</div>
<StageColumn
v-for="sid in col"
:key="sid"
:stage-id="sid"
:query="query"
:category="category"
:column-index="colIndex"
/>
</div>
</div>
<div
v-if="!columns.length || !totalStages"
class="absolute inset-0 flex flex-col items-center justify-center gap-4 text-slate-400"
>
<UIcon name="i-heroicons-queue-list-20-solid" class="h-10 w-10 text-slate-600" />
<div class="text-sm">暂无阶段点击添加阶段开始构建看板</div>
</div>
</div>
</div>
<div v-if="!columns.length || !columns[0]?.length" class="text-center text-slate-400 py-10">
暂无阶段点击左侧添加阶段创建
</div>
<div class="px-3 pb-3">
<button class="px-3 py-1 rounded bg-slate-800/60 border border-border hover:bg-slate-800" @click="addColumn">添加列</button>
</div>
<div class="px-3 pb-3">
<button class="px-3 py-1 rounded bg-slate-800/60 border border-border hover:bg-slate-800" @click="addStage">添加阶段</button>
</div>
</UCard>
<StageModal v-model:open="stageModalOpen" :default-column="stageModalColumn" @submit="createStage" />
</template>
<script setup lang="ts">
import { useBoardStore } from '~/stores/board'
import StageColumn from './StageColumn.vue'
import StageModal from '~/components/dialogs/StageModal.vue'
const store = useBoardStore()
const props = defineProps<{ query: string; category: string | '' }>()
const columns = computed(() => store.board.layout?.columns || [])
const gridStyle = computed(() => ({
gridAutoFlow: 'column',
gridAutoRows: '1fr'
}))
const props = defineProps<{ query: string; category: string | '' }>()
const toast = useToast()
const columns = computed(() => store.board.layout?.columns || [])
const totalStages = computed(() => store.board.stages.length)
const stageModalOpen = ref(false)
const stageModalColumn = ref(0)
function openStageModal(column = 0) {
stageModalColumn.value = column
stageModalOpen.value = true
}
function createStage(payload: { title: string; column: number }) {
stageModalOpen.value = false
const column = Math.max(0, Math.min(payload.column, columns.value.length ? columns.value.length - 1 : 0))
store.addStage(payload.title.trim(), column)
toast.add({ color: 'primary', title: `已创建阶段「${payload.title}` })
}
function addColumn() {
if (!store.board.layout.columns) store.board.layout.columns = []
store.board.layout.columns.push([])
store.log('column-add', { count: store.board.layout.columns.length })
}
function addStage() {
const title = prompt('阶段名称?')
if (!title) return
const colIndex = store.board.layout.columns.length ? 0 : 0
store.addStage(title.trim(), colIndex)
toast.add({ color: 'neutral', title: `新增列,当前共有 ${store.board.layout.columns.length}` })
}
</script>