chore: update project files
This commit is contained in:
111
web/src/components/TodoEditorModal.vue
Normal file
111
web/src/components/TodoEditorModal.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, watch } from 'vue'
|
||||
import type { Task } from '../types'
|
||||
import { t } from '../i18n'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
editingTask?: Task | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [boolean]
|
||||
submit: [payload: Partial<Task>]
|
||||
}>()
|
||||
|
||||
const form = reactive({
|
||||
title: '',
|
||||
description: '',
|
||||
due_at: '',
|
||||
priority: 2,
|
||||
tags: '',
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.editingTask,
|
||||
(task) => {
|
||||
form.title = task?.title ?? ''
|
||||
form.description = task?.description ?? ''
|
||||
form.due_at = toLocal(task?.due_at ?? '')
|
||||
form.priority = task?.priority ?? 2
|
||||
form.tags = task?.tags?.join(', ') ?? ''
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(open) => {
|
||||
if (!open && !props.editingTask) {
|
||||
form.title = ''
|
||||
form.description = ''
|
||||
form.due_at = ''
|
||||
form.priority = 2
|
||||
form.tags = ''
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
function close() {
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
function save() {
|
||||
emit('submit', {
|
||||
title: form.title,
|
||||
description: form.description,
|
||||
due_at: form.due_at ? new Date(form.due_at).toISOString() : '',
|
||||
priority: Number(form.priority),
|
||||
tags: form.tags
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean),
|
||||
})
|
||||
}
|
||||
|
||||
function toLocal(v: string) {
|
||||
if (!v) return ''
|
||||
const d = new Date(v)
|
||||
if (Number.isNaN(d.getTime())) return ''
|
||||
const offset = d.getTimezoneOffset() * 60000
|
||||
return new Date(d.getTime() - offset).toISOString().slice(0, 16)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="modelValue" class="modal-mask" @click.self="close">
|
||||
<section class="modal">
|
||||
<header>
|
||||
<h3>{{ editingTask ? t('edit_todo') : t('create_todo') }}</h3>
|
||||
</header>
|
||||
<label>
|
||||
{{ t('title') }}
|
||||
<input v-model="form.title" type="text" required />
|
||||
</label>
|
||||
<label>
|
||||
{{ t('due_at') }}
|
||||
<input v-model="form.due_at" type="datetime-local" />
|
||||
</label>
|
||||
<label>
|
||||
{{ t('priority') }}
|
||||
<select v-model="form.priority">
|
||||
<option :value="1">{{ t('high') }}</option>
|
||||
<option :value="2">{{ t('medium') }}</option>
|
||||
<option :value="3">{{ t('low') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
{{ t('tags') }}
|
||||
<input v-model="form.tags" type="text" :placeholder="t('tags_placeholder')" />
|
||||
</label>
|
||||
<label>
|
||||
{{ t('description') }}
|
||||
<textarea v-model="form.description" rows="4"></textarea>
|
||||
</label>
|
||||
<div class="modal-actions">
|
||||
<button class="btn" @click="close">{{ t('cancel') }}</button>
|
||||
<button class="btn primary" @click="save">{{ t('save') }}</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user