Skip to content

Todo List

todo-list-v2

@/pages/40-lrn/20-vue3/90-demos/20-todo-list/components/TodoListV2.vue

待办
vue
<template>
    <div>
        <div v-if="isEditing" class="todo-edit">
            <div>标题</div>
            <div>
                <input type="text" v-model="form.title" />
            </div>
            <div>内容</div>
            <div>
                <textarea v-model="form.content"></textarea>
            </div>
            <div>
                <button @click="onCancelClick">返回</button>&nbsp;
                <button @click="onSaveClick" :disabled="!form.title && !form.content">保存</button>
            </div>
        </div>
        <div v-else class="todo-list">
            <div>
                待办
                <button @click="onAddClick">+</button>
            </div>
            <div class="todo-list-items">
                <div v-for="item in todoList" class="todo-list-item">
                    <h3>
                        <input
                            type="checkbox"
                            v-model="item.status"
                            @change="$event => onCheckChange($event, item)"
                        />
                        {{ item.title }}
                        <span style="font-size: small;">{{ item.updatedAt || item.createdAt }}</span>
                    </h3>
                    <p>{{ item.content }}</p>
                    <div>
                        <button @click="onEditClick(item)">编辑</button>&nbsp;
                        <button @click="onDeleteClick(item)">删除</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts">
import { defineComponent } from "vue"

function newForm() {
    return { id: undefined, title: undefined, content: undefined, createdAt: undefined, updatedAt: undefined, status: undefined, };
}

export default defineComponent({
    name: 'TodoListV2',
    data() {
        return {
            isEditing: false,
            form: newForm(),
            todoList: [],
        }
    },
    methods: {
        onCancelClick() {
            this.isEditing = false;
            this.form = newForm();
        },
        onSaveClick() {
            this.isEditing = false;
            if (!this.form.id) {
                this.todoList.push({ ...this.form, id: Date.now(), createdAt: new Date().toLocaleString(), });
            } else {
                let oldIndex = this.todoList.findIndex(o => o.id === this.form.id);
                if (oldIndex > -1) {
                    this.todoList.splice(oldIndex, 1, { ...this.form, updatedAt: new Date().toLocaleString(), });
                } else {
                    this.todoList.push({ ...this.form, updatedAt: new Date().toLocaleString(), });
                }
            }
            localStorage.setItem('todoListV2', JSON.stringify(this.todoList));

            this.form = newForm();
        },
        onEditClick(item) {
            this.form = { ...item, };
            this.isEditing = true;
        },
        onAddClick() {
            this.form = newForm();
            this.isEditing = true;
        },
        onDeleteClick(item) {
            let oldIndex = this.todoList.findIndex(o => o.id === item.id);
            if (oldIndex > -1) {
                this.todoList.splice(oldIndex, 1);
            }
            localStorage.setItem('todoListV2', JSON.stringify(this.todoList));
        },
        onCheckChange($event, item) {
            item.status = $event.target.checked;
            localStorage.setItem('todoListV2', JSON.stringify(this.todoList));
        },
    },
    mounted() {
        let _todoList = JSON.parse(localStorage.getItem('todoListV2') || '[]');
        this.todoList.push(..._todoList);
    },
})
</script>

<style scoped>
.todo-list-items {
    max-height: 160px;
    overflow: auto;
}
</style>

todo-list-v3

@/pages/40-lrn/20-vue3/90-demos/20-todo-list/components/TodoListV3.vue

待办
vue
<template>
    <div>
        <div v-if="isEditing" class="todo-edit">
            <div>标题</div>
            <div>
                <input type="text" v-model="form.title" />
            </div>
            <div>内容</div>
            <div>
                <textarea v-model="form.content"></textarea>
            </div>
            <div>
                <button @click="onCancelClick">返回</button>&nbsp;
                <button @click="onSaveClick" :disabled="!form.title && !form.content">保存</button>
            </div>
        </div>
        <div v-else class="todo-list">
            <div>
                待办
                <button @click="onAddClick">+</button>
            </div>
            <div class="todo-list-items">
                <div v-for="item in todoList" class="todo-list-item">
                    <h3>
                        <input
                            type="checkbox"
                            v-model="item.status"
                            @change="$event => onCheckChange($event, item)"
                        />
                        {{ item.title }}
                        <span style="font-size: small;">{{ item.updatedAt || item.createdAt }}</span>
                    </h3>
                    <p>{{ item.content }}</p>
                    <div>
                        <button @click="onEditClick(item)">编辑</button>&nbsp;
                        <button @click="onDeleteClick(item)">删除</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts">
import { defineComponent, reactive, ref, onMounted } from "vue"

export default defineComponent({
    setup(props, ctx) {
        // data
        const form = reactive(newForm());
        const isEditing = ref(false);
        const todoList = reactive([newForm()].slice(1));

        function newForm() {
            return { id: undefined, title: undefined, content: undefined, createdAt: undefined, updatedAt: undefined, status: undefined, };
        }

        // methods
        const onCancelClick = () => {
            isEditing.value = false;
            Object.assign(form, newForm());
        };
        const onSaveClick = () => {
            isEditing.value = false;
            if (!form.id) {
                todoList.push({ ...form, id: Date.now(), createdAt: new Date().toLocaleString() });
            } else {
                let oldIndex = todoList.findIndex(o => o.id === form.id);
                if (oldIndex > -1) {
                    todoList.splice(oldIndex, 1, { ...form, updatedAt: new Date().toLocaleString() });
                } else {
                    todoList.push({ ...form, updatedAt: new Date().toLocaleString() });
                }
            }
            localStorage.setItem('todoListV3', JSON.stringify(todoList));

            Object.assign(form, newForm());
        }
        const onEditClick = (item) => {
            Object.assign(form, item);
            isEditing.value = true;
        }
        const onAddClick = () => {
            Object.assign(form, newForm());
            isEditing.value = true;
        }
        const onDeleteClick = (item) => {
            let oldIndex = todoList.findIndex(o => o.id === item.id);
            if (oldIndex > -1) {
                todoList.splice(oldIndex, 1);
            }
            localStorage.setItem('todoListV3', JSON.stringify(todoList));
        }
        function onCheckChange($event, item) {
            item.status = $event.target.checked;
            localStorage.setItem('todoListV3', JSON.stringify(todoList));
        }

        onMounted(() => {
            let _todoList = JSON.parse(localStorage.getItem('todoListV3') || '[]');
            todoList.push(..._todoList);
        });

        return {
            form,
            isEditing,
            todoList,
            onCancelClick,
            onSaveClick,
            onEditClick,
            onAddClick,
            onDeleteClick,
            onCheckChange,
        }
    },
})
</script>

<style scoped>
.todo-list-items {
    max-height: 160px;
    overflow: auto;
}
</style>

todo-list-v3-setup

@/pages/40-lrn/20-vue3/90-demos/20-todo-list/components/TodoListV3Setup.vue

待办
vue
<template>
    <div>
        <div v-if="isEditing" class="todo-edit">
            <div>标题</div>
            <div>
                <input type="text" v-model="form.title" />
            </div>
            <div>内容</div>
            <div>
                <textarea v-model="form.content"></textarea>
            </div>
            <div>
                <button @click="onCancelClick">返回</button>&nbsp;
                <button @click="onSaveClick" :disabled="!form.title && !form.content">保存</button>
            </div>
        </div>
        <div v-else class="todo-list">
            <div>
                待办
                <button @click="onAddClick">+</button>
            </div>
            <div class="todo-list-items">
                <div v-for="item in todoList" class="todo-list-item">
                    <h3>
                        <input type="checkbox" v-model="item.status" @change="$event => onCheckChange($event, item)" />
                        {{ item.title }}
                        <span style="font-size: small;">{{ item.updatedAt || item.createdAt }}</span>
                    </h3>
                    <p>{{ item.content }}</p>
                    <div>
                        <button @click="onEditClick(item)">编辑</button>&nbsp;
                        <button @click="onDeleteClick(item)">删除</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { reactive, ref, onMounted } from "vue"

// data
const form = reactive(newForm());
const isEditing = ref(false);
const todoList = reactive([newForm()].slice(1));

function newForm() {
    return { id: undefined, title: undefined, content: undefined, createdAt: undefined, updatedAt: undefined, status: undefined, };
}

// methods
const onCancelClick = () => {
    isEditing.value = false;
    Object.assign(form, newForm());
};
const onSaveClick = () => {
    isEditing.value = false;
    if (!form.id) {
        todoList.push({ ...form, id: Date.now(), createdAt: new Date().toLocaleString() });
    } else {
        let oldIndex = todoList.findIndex(o => o.id === form.id);
        if (oldIndex > -1) {
            todoList.splice(oldIndex, 1, { ...form, updatedAt: new Date().toLocaleString() });
        } else {
            todoList.push({ ...form, updatedAt: new Date().toLocaleString() });
        }
    }
    localStorage.setItem('todoListV3Setup', JSON.stringify(todoList));

    Object.assign(form, newForm());
}
const onEditClick = (item) => {
    Object.assign(form, item);
    isEditing.value = true;
}
const onAddClick = () => {
    Object.assign(form, newForm());
    isEditing.value = true;
}
const onDeleteClick = (item) => {
    let oldIndex = todoList.findIndex(o => o.id === item.id);
    if (oldIndex > -1) {
        todoList.splice(oldIndex, 1);
    }
    localStorage.setItem('todoListV3Setup', JSON.stringify(todoList));
}
function onCheckChange($event, item) {
    item.status = $event.target.checked;
    localStorage.setItem('todoListV3Setup', JSON.stringify(todoList));
}

onMounted(() => {
    let _todoList = JSON.parse(localStorage.getItem('todoListV3Setup') || '[]');
    todoList.push(..._todoList);
});
</script>

<style scoped>
.todo-list-items {
    max-height: 160px;
    overflow: auto;
}
</style>