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:
92
components/panels/HistoryPanel.vue
Normal file
92
components/panels/HistoryPanel.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-clock-20-solid" class="h-5 w-5 text-amber-400" />
|
||||
<span class="font-semibold">历史记录</span>
|
||||
</div>
|
||||
<UButton
|
||||
size="2xs"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
icon="i-heroicons-trash-20-solid"
|
||||
@click="clearOpen = true"
|
||||
>
|
||||
清空
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div v-if="!entries.length" class="rounded border border-slate-800 bg-slate-900/70 px-3 py-4 text-sm text-slate-400">
|
||||
暂无历史记录。
|
||||
</div>
|
||||
<div v-else class="max-h-72 space-y-2 overflow-auto pr-1 text-xs">
|
||||
<div
|
||||
v-for="entry in entries"
|
||||
:key="entry.id"
|
||||
class="rounded border border-slate-800 bg-slate-900/70 px-3 py-3"
|
||||
>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="font-medium text-slate-200">{{ entry.type }}</span>
|
||||
<span class="text-[10px] text-slate-500">{{ formatTime(entry.ts) }}</span>
|
||||
</div>
|
||||
<div class="mt-1 flex items-center gap-2 text-[11px] text-slate-400">
|
||||
<UIcon name="i-heroicons-user-20-solid" class="h-3.5 w-3.5" />
|
||||
<span>{{ entry.actor || '系统' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UModal v-model="clearOpen">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-exclamation-triangle-20-solid" class="h-5 w-5 text-amber-400" />
|
||||
<span class="font-semibold">清空历史</span>
|
||||
</div>
|
||||
</template>
|
||||
<p class="text-sm text-slate-300">
|
||||
清空后将只能在本地重新积累记录。仍然继续吗?
|
||||
</p>
|
||||
<div class="mt-6 flex justify-end gap-2">
|
||||
<UButton color="neutral" variant="ghost" @click="clearOpen = false">取消</UButton>
|
||||
<UButton color="rose" @click="clearHistory">确认清空</UButton>
|
||||
</div>
|
||||
</UCard>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useBoardStore } from '~/stores/board'
|
||||
|
||||
const store = useBoardStore()
|
||||
const toast = useToast()
|
||||
|
||||
const entries = computed(() => {
|
||||
const list = store.board.meta?.history || []
|
||||
return list.slice(-200).reverse()
|
||||
})
|
||||
|
||||
function formatTime(ts: string) {
|
||||
try {
|
||||
return new Intl.DateTimeFormat('zh-CN', { dateStyle: 'short', timeStyle: 'short' }).format(new Date(ts))
|
||||
} catch {
|
||||
return ts
|
||||
}
|
||||
}
|
||||
|
||||
const clearOpen = ref(false)
|
||||
|
||||
function clearHistory() {
|
||||
clearOpen.value = false
|
||||
if (!store.board.meta) return
|
||||
store.board.meta.history = []
|
||||
store.board.meta.modifiedAt = new Date().toISOString()
|
||||
store.log('history-clear', {})
|
||||
toast.add({ color: 'neutral', title: '历史记录已清空' })
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user