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:
@@ -1,55 +1,84 @@
|
||||
<template>
|
||||
<div v-if="open" class="fixed inset-0 bg-black/60 flex items-center justify-center p-4">
|
||||
<div class="w-[1000px] max-w-[95vw] max-h-[90vh] rounded-lg border border-border bg-panel flex flex-col">
|
||||
<div class="flex items-center justify-between px-3 py-2 border-b border-border">
|
||||
<h2 class="font-semibold">导入/合并 预览</h2>
|
||||
<button class="px-2 py-1 rounded bg-slate-800/60 border border-border hover:bg-slate-800" @click="close">✕</button>
|
||||
</div>
|
||||
<div class="p-3 overflow-auto space-y-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<div>
|
||||
<label class="text-sm text-slate-400 mr-2">策略</label>
|
||||
<select v-model="policy" class="px-2 py-1 rounded bg-slate-900 border border-border">
|
||||
<option value="prefer-import">冲突优先:导入文件</option>
|
||||
<option value="prefer-current">冲突优先:当前看板</option>
|
||||
</select>
|
||||
<UModal v-model="open" :ui="{ width: 'max-w-3xl' }">
|
||||
<UCard>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-arrow-down-tray-20-solid" class="h-5 w-5 text-sky-400" />
|
||||
<span class="font-semibold">导入/合并预览</span>
|
||||
</div>
|
||||
<label class="text-sm flex items-center gap-2">
|
||||
<input type="checkbox" v-model="removeMissing" /> 同步删除在导入文件中不存在的任务/阶段
|
||||
</label>
|
||||
<UBadge color="neutral" variant="soft">来源文件:{{ name || '未命名' }}</UBadge>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="rounded border border-border bg-slate-900 p-3">
|
||||
<div class="text-sm text-slate-400">任务新增</div>
|
||||
<div class="text-2xl font-bold">{{ diff?.tasks?.added?.length || 0 }}</div>
|
||||
</div>
|
||||
<div class="rounded border border-border bg-slate-900 p-3">
|
||||
<div class="text-sm text-slate-400">任务删除</div>
|
||||
<div class="text-2xl font-bold">{{ diff?.tasks?.removed?.length || 0 }}</div>
|
||||
</div>
|
||||
<div class="rounded border border-border bg-slate-900 p-3">
|
||||
<div class="text-sm text-slate-400">任务修改</div>
|
||||
<div class="text-2xl font-bold">{{ diff?.tasks?.modified?.length || 0 }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="space-y-6">
|
||||
<UAlert
|
||||
color="primary"
|
||||
variant="soft"
|
||||
title="合并前请确认策略"
|
||||
description="导入文件将与当前看板数据对比。请选择冲突策略及是否删除缺失项目。"
|
||||
/>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<UFormGroup label="冲突策略" name="policy" class="w-64">
|
||||
<USelectMenu
|
||||
v-model="policy"
|
||||
:options="policies"
|
||||
value-attribute="value"
|
||||
option-attribute="label"
|
||||
/>
|
||||
</UFormGroup>
|
||||
<UCheckbox v-model="removeMissing">
|
||||
同步删除在导入文件中不存在的任务/阶段
|
||||
</UCheckbox>
|
||||
</div>
|
||||
<div class="rounded border border-border bg-slate-900 p-3 text-xs whitespace-pre-wrap">
|
||||
<div>文件:{{ name }}</div>
|
||||
<div>Tasks: +{{ diff?.tasks?.added?.length || 0 }} -{{ diff?.tasks?.removed?.length || 0 }} ~{{ diff?.tasks?.modified?.length || 0 }}</div>
|
||||
<div>Stages: +{{ diff?.stages?.added?.length || 0 }} -{{ diff?.stages?.removed?.length || 0 }} ~{{ diff?.stages?.modified?.length || 0 }}</div>
|
||||
<div>Layout changed: {{ diff?.layout?.changed ? 'Yes' : 'No' }}</div>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-3">
|
||||
<UCard variant="soft" class="border border-emerald-500/20 bg-emerald-500/5 text-emerald-200">
|
||||
<p class="text-xs uppercase tracking-wide opacity-70">任务新增</p>
|
||||
<p class="text-3xl font-semibold">{{ diff?.tasks?.added?.length || 0 }}</p>
|
||||
</UCard>
|
||||
<UCard variant="soft" class="border border-rose-500/20 bg-rose-500/5 text-rose-200">
|
||||
<p class="text-xs uppercase tracking-wide opacity-70">任务删除</p>
|
||||
<p class="text-3xl font-semibold">{{ diff?.tasks?.removed?.length || 0 }}</p>
|
||||
</UCard>
|
||||
<UCard variant="soft" class="border border-amber-500/20 bg-amber-500/5 text-amber-200">
|
||||
<p class="text-xs uppercase tracking-wide opacity-70">任务修改</p>
|
||||
<p class="text-3xl font-semibold">{{ diff?.tasks?.modified?.length || 0 }}</p>
|
||||
</UCard>
|
||||
</div>
|
||||
|
||||
<UCard variant="soft" class="border border-slate-800 bg-slate-900/70 text-xs text-slate-300">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-document-duplicate-20-solid" class="h-4 w-4 text-slate-500" />
|
||||
<span>Tasks: +{{ diff?.tasks?.added?.length || 0 }} / -{{ diff?.tasks?.removed?.length || 0 }} / ~{{ diff?.tasks?.modified?.length || 0 }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-queue-list-20-solid" class="h-4 w-4 text-slate-500" />
|
||||
<span>Stages: +{{ diff?.stages?.added?.length || 0 }} / -{{ diff?.stages?.removed?.length || 0 }} / ~{{ diff?.stages?.modified?.length || 0 }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-view-columns-20-solid" class="h-4 w-4 text-slate-500" />
|
||||
<span>布局是否变化:{{ diff?.layout?.changed ? '是' : '否' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<div class="flex justify-end gap-2">
|
||||
<UButton color="neutral" variant="ghost" @click="open = false">取消</UButton>
|
||||
<UButton color="primary" icon="i-heroicons-arrow-up-tray-20-solid" @click="apply">应用合并</UButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 px-3 py-2 border-t border-border">
|
||||
<button class="px-3 py-1 rounded bg-accent text-slate-900 font-medium hover:brightness-110" @click="apply">应用合并</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useBoardStore } from '~/stores/board'
|
||||
|
||||
const store = useBoardStore()
|
||||
|
||||
const open = defineModel<boolean>('open', { required: true })
|
||||
const imported = defineModel<any>('imported', { required: true })
|
||||
const diff = defineModel<any>('diff', { required: true })
|
||||
@@ -57,10 +86,14 @@ const name = defineModel<string>('name', { required: true })
|
||||
|
||||
const policy = ref<'prefer-import' | 'prefer-current'>('prefer-import')
|
||||
const removeMissing = ref(false)
|
||||
function close(){ open.value = false }
|
||||
function apply(){
|
||||
|
||||
const policies = [
|
||||
{ label: '冲突优先:导入文件', value: 'prefer-import' },
|
||||
{ label: '冲突优先:当前看板', value: 'prefer-current' }
|
||||
]
|
||||
|
||||
function apply() {
|
||||
store.applyMerge(imported.value, policy.value, removeMissing.value)
|
||||
close()
|
||||
open.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user