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.
40 lines
1.8 KiB
Vue
40 lines
1.8 KiB
Vue
<template>
|
|
<div class="grid grid-cols-[20px_1fr_auto] items-center gap-2 p-2 rounded-lg bg-card border border-border shadow" draggable="true" data-task :data-id="taskId" @dragstart="onDragStart">
|
|
<div class="opacity-70 select-none cursor-grab">⋮⋮</div>
|
|
<div>
|
|
<div class="font-semibold">{{ task?.title || '(无标题)' }}</div>
|
|
<div class="text-xs text-slate-400">步骤: {{ stat }}</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-[11px] px-2 py-0.5 rounded-full border border-black/30 whitespace-nowrap" :style="chipStyle">{{ categoryTitle }}</span>
|
|
<button class="px-2 py-1 rounded bg-slate-800/60 border border-border hover:bg-slate-800" @click="openModal">编辑</button>
|
|
</div>
|
|
</div>
|
|
<TaskModal v-if="open" :task-id="taskId" @close="open=false" />
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useBoardStore } from '~/stores/board'
|
|
import TaskModal from './TaskModal.vue'
|
|
|
|
const store = useBoardStore()
|
|
const props = defineProps<{ taskId: string }>()
|
|
const task = computed(() => store.taskById(props.taskId))
|
|
const category = computed(() => store.categoryById(task.value?.category as string))
|
|
const categoryTitle = computed(() => category.value?.title || '未分类')
|
|
const chipStyle = computed(() => ({ background: `#${(category.value?.color||'888888')}33`, borderColor: `#${(category.value?.color||'888888')}` }))
|
|
const stat = computed(() => {
|
|
const steps = task.value?.steps || []
|
|
const done = steps.filter(s => s.done).length
|
|
return `${done}/${steps.length}`
|
|
})
|
|
const open = ref(false)
|
|
function openModal(){ open.value = true }
|
|
function onDragStart(e: DragEvent) {
|
|
e.dataTransfer?.setData('text/task', props.taskId)
|
|
e.dataTransfer!.effectAllowed = 'move'
|
|
emit('dragging', true)
|
|
}
|
|
const emit = defineEmits<{ (e:'dragging', v:boolean): void }>()
|
|
</script>
|