Files
kanban/components/MergeModal.vue
xiaomai 485d75820b 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.
2025-10-22 17:52:17 +08:00

100 lines
4.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>