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.
74 lines
2.6 KiB
Vue
74 lines
2.6 KiB
Vue
<template>
|
||
<UCard>
|
||
<template #header>
|
||
<div class="flex items-center justify-between">
|
||
<div class="flex items-center gap-2">
|
||
<UIcon name="i-heroicons-chart-bar-20-solid" class="h-5 w-5 text-emerald-400" />
|
||
<span class="font-semibold">看板概览</span>
|
||
</div>
|
||
<UBadge
|
||
v-if="lastModified"
|
||
:label="`更新于:${lastModified}`"
|
||
color="neutral"
|
||
variant="soft"
|
||
class="text-xs"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<div class="grid grid-cols-3 gap-3 text-sm">
|
||
<UCard variant="soft" class="border border-emerald-500/10 bg-emerald-500/5 text-emerald-200">
|
||
<div class="space-y-1">
|
||
<p class="text-xs uppercase tracking-wide opacity-80">任务</p>
|
||
<p class="text-2xl font-semibold">{{ tasksCount }}</p>
|
||
</div>
|
||
</UCard>
|
||
<UCard variant="soft" class="border border-sky-500/10 bg-sky-500/5 text-sky-200">
|
||
<div class="space-y-1">
|
||
<p class="text-xs uppercase tracking-wide opacity-80">阶段</p>
|
||
<p class="text-2xl font-semibold">{{ stagesCount }}</p>
|
||
</div>
|
||
</UCard>
|
||
<UCard variant="soft" class="border border-violet-500/10 bg-violet-500/5 text-violet-200">
|
||
<div class="space-y-1">
|
||
<p class="text-xs uppercase tracking-wide opacity-80">列</p>
|
||
<p class="text-2xl font-semibold">{{ columnsCount }}</p>
|
||
</div>
|
||
</UCard>
|
||
</div>
|
||
|
||
<div class="mt-4 space-y-2 text-xs text-slate-400">
|
||
<div class="flex items-center gap-2">
|
||
<UIcon name="i-heroicons-user-circle-20-solid" class="h-4 w-4 text-slate-500" />
|
||
<span>当前操作人:{{ actor || '未设置' }}</span>
|
||
</div>
|
||
<div class="flex items-center gap-2">
|
||
<UIcon name="i-heroicons-document-duplicate-20-solid" class="h-4 w-4 text-slate-500" />
|
||
<span>文件来源:{{ filename }}</span>
|
||
</div>
|
||
</div>
|
||
</UCard>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { useBoardStore } from '~/stores/board'
|
||
|
||
const store = useBoardStore()
|
||
|
||
const columnsCount = computed(() => store.board.layout?.columns?.length || 0)
|
||
const stagesCount = computed(() => store.board.stages.length)
|
||
const tasksCount = computed(() => store.board.tasks.length)
|
||
const actor = computed(() => store.board.meta?.actor || '')
|
||
const filename = computed(() => store.filename || '未命名')
|
||
|
||
const lastModified = computed(() => {
|
||
const ts = store.board.meta?.modifiedAt
|
||
if (!ts) return ''
|
||
try {
|
||
return new Intl.DateTimeFormat('zh-CN', { dateStyle: 'short', timeStyle: 'short' }).format(new Date(ts))
|
||
} catch {
|
||
return ts
|
||
}
|
||
})
|
||
</script>
|