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.
96 lines
2.6 KiB
Vue
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>
|