refactor(app): rewrite application with Nuxt 4, Pinia, and TailwindCSS
This commit replaces the original vanilla JavaScript implementation with a modern frontend stack. The entire application has been rewritten to improve maintainability, developer experience, and leverage modern web technologies. Key changes include: - Replaced plain HTML, CSS, and JS with a Nuxt 4 project structure. - Migrated state management from a global state object to a Pinia store for better organization and reactivity. - Rebuilt the UI with Vue 3 components and styled with TailwindCSS. - Changed the primary persistence mechanism from manual file I/O to automatic LocalStorage saving, while retaining import/export functionality. - All core features, including drag-and-drop, task management, and the import/merge diffing logic, have been ported to the new architecture.
This commit is contained in:
68
components/TaskModal.vue
Normal file
68
components/TaskModal.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="fixed inset-0 bg-black/60 flex items-center justify-center p-4">
|
||||
<div class="w-[720px] 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="$emit('close')">✕</button>
|
||||
</div>
|
||||
<div class="p-3 overflow-auto space-y-3">
|
||||
<div class="grid grid-cols-[90px_1fr] gap-3 items-start">
|
||||
<label class="pt-1 text-sm text-slate-400">标题</label>
|
||||
<input v-model="title" class="px-2 py-1 rounded bg-slate-900 border border-border" />
|
||||
</div>
|
||||
<div class="grid grid-cols-[90px_1fr] gap-3 items-start">
|
||||
<label class="pt-1 text-sm text-slate-400">类别</label>
|
||||
<select v-model="category" class="px-2 py-1 rounded bg-slate-900 border border-border">
|
||||
<option value="">未分类</option>
|
||||
<option v-for="c in store.board.categories" :key="c.uuid" :value="c.uuid">{{ c.title }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-[90px_1fr] gap-3 items-start">
|
||||
<label class="pt-1 text-sm text-slate-400">描述</label>
|
||||
<textarea v-model="desc" rows="6" class="px-2 py-1 rounded bg-slate-900 border border-border"></textarea>
|
||||
</div>
|
||||
<div class="grid grid-cols-[90px_1fr] gap-3 items-start">
|
||||
<label class="pt-1 text-sm text-slate-400">步骤</label>
|
||||
<div class="space-y-2">
|
||||
<div v-for="(s, i) in steps" :key="i" class="grid grid-cols-[24px_1fr_24px] items-center gap-2">
|
||||
<input type="checkbox" v-model="s.done" />
|
||||
<input v-model="s.details" class="px-2 py-1 rounded bg-slate-900 border border-border" />
|
||||
<button class="text-sm" @click="steps.splice(i,1)">🗑</button>
|
||||
</div>
|
||||
<button class="px-2 py-1 rounded bg-slate-800/60 border border-border hover:bg-slate-800" @click="steps.push({details:'新步骤', done:false})">添加步骤</button>
|
||||
</div>
|
||||
</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-red-500/90 text-white hover:brightness-110" @click="onDelete">删除</button>
|
||||
<div class="flex-1" />
|
||||
<button class="px-3 py-1 rounded bg-accent text-slate-900 font-medium hover:brightness-110" @click="onSave">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useBoardStore } from '~/stores/board'
|
||||
|
||||
const store = useBoardStore()
|
||||
const props = defineProps<{ taskId: string }>()
|
||||
const t = computed(() => store.taskById(props.taskId))
|
||||
const title = ref(t.value?.title || '')
|
||||
const desc = ref(t.value?.description || '')
|
||||
const category = ref<string | ''>((t.value?.category as string) || '')
|
||||
const steps = ref(JSON.parse(JSON.stringify(t.value?.steps || [])))
|
||||
|
||||
function onSave() {
|
||||
store.editTask(props.taskId, { title: title.value.trim() || t.value?.title, description: desc.value, category: category.value || null, steps: steps.value })
|
||||
emit('close')
|
||||
}
|
||||
function onDelete() {
|
||||
if (!confirm('删除该任务?此操作不可撤销')) return
|
||||
store.removeTask(props.taskId)
|
||||
emit('close')
|
||||
}
|
||||
const emit = defineEmits(['close'])
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user