Files
kanban/pages/index.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

96 lines
2.6 KiB
Vue

<template>
<UContainer class="mx-auto max-w-7xl space-y-6 py-8">
<Toolbar v-model:query="query" v-model:category="category" @open-merge="openMerge" />
<div class="grid gap-6 xl:grid-cols-[320px_1fr]">
<div class="space-y-6">
<CategoryPanel />
<BoardSummaryPanel />
<HistoryPanel />
</div>
<div class="space-y-6">
<Board :query="query" :category="category" />
</div>
</div>
<MergeModal
v-model:open="mergeOpen"
v-model:imported="imported"
v-model:diff="diff"
v-model:name="importName"
/>
</UContainer>
</template>
<script setup lang="ts">
import Toolbar from '~/components/Toolbar.vue'
import Board from '~/components/Board.vue'
import MergeModal from '~/components/MergeModal.vue'
import CategoryPanel from '~/components/panels/CategoryPanel.vue'
import BoardSummaryPanel from '~/components/panels/BoardSummaryPanel.vue'
import HistoryPanel from '~/components/panels/HistoryPanel.vue'
import { useBoardStore } from '~/stores/board'
const store = useBoardStore()
const config = useRuntimeConfig().public
const toast = useToast()
const query = ref('')
const category = ref('')
const mergeOpen = ref(false)
const imported = ref<any>({})
const diff = ref<any>({})
const importName = ref('导入文件')
function openMerge(data: any, d: any, name: string) {
imported.value = data
diff.value = d
importName.value = name
mergeOpen.value = true
}
async function bootstrap() {
const currentUrl = typeof window !== 'undefined' ? window.location.href : ''
const qp = currentUrl ? new URL(currentUrl).searchParams.get('file') : null
const fileToLoad = qp || config.autoLoadFile || ''
if (fileToLoad) {
try {
const res = await fetch(fileToLoad, { cache: 'no-store' })
if (res.ok) {
const data = await res.json()
store.setBoard(data, fileToLoad)
store.log('load-file', { name: fileToLoad, via: qp ? 'query' : 'config' })
toast.add({ color: 'primary', title: `已自动加载 ${fileToLoad}` })
return
}
} catch (error: any) {
toast.add({ color: 'rose', title: '自动加载失败', description: error?.message })
}
}
if (!store.loadFromLocal()) {
store.setBoard(
{
categories: [],
stages: [],
tasks: [],
layout: { columns: [] },
meta: {
id: 'open-kanban',
version: '1.0.0',
createdAt: new Date().toISOString(),
modifiedAt: new Date().toISOString(),
actor: '',
history: []
}
},
''
)
}
}
onMounted(() => {
bootstrap()
})
</script>