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.
100 lines
4.3 KiB
Vue
100 lines
4.3 KiB
Vue
<template>
|
||
<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>
|
||
<UBadge color="neutral" variant="soft">来源文件:{{ name || '未命名' }}</UBadge>
|
||
</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="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>
|
||
</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 })
|
||
const name = defineModel<string>('name', { required: true })
|
||
|
||
const policy = ref<'prefer-import' | 'prefer-current'>('prefer-import')
|
||
const removeMissing = ref(false)
|
||
|
||
const policies = [
|
||
{ label: '冲突优先:导入文件', value: 'prefer-import' },
|
||
{ label: '冲突优先:当前看板', value: 'prefer-current' }
|
||
]
|
||
|
||
function apply() {
|
||
store.applyMerge(imported.value, policy.value, removeMissing.value)
|
||
open.value = false
|
||
}
|
||
</script>
|