refactor:use_pinia

This commit is contained in:
dichgrem
2025-12-01 11:16:39 +08:00
parent d6278e4451
commit f734593ff6
8 changed files with 329 additions and 144 deletions

View File

@@ -1,89 +1,60 @@
<script>
<script setup>
import TodoHeader from "./components/TodoHeader.vue";
import TodoList from "./components/TodoList.vue";
import TodoFooter from "./components/TodoFooter.vue";
export default {
components: {
TodoHeader,
TodoList,
TodoFooter,
},
import { useTodoStore } from "./stores/todo";
import { computed } from "vue";
data() {
return {
// 从 localStorage 初始化
todos: JSON.parse(localStorage.getItem("vue-todos") || "[]"),
tabType: 0, // 0全部 1未完成 2已完成
};
},
const todoStore = useTodoStore();
computed: {
// 筛选后的列表
todoList() {
if (this.tabType === 0) return this.todos;
const type = this.tabType;
return this.todos.filter((item) => {
if (type === 1) return !item.completed;
return item.completed;
});
},
},
function addTodo(newTodo) {
console.log("App.vue addTodo:", newTodo);
todoStore.addTodo(newTodo);
}
methods: {
addTodo(newTodo) {
const todo = {
id: Date.now(),
txt: newTodo,
completed: false,
};
this.todos.push(todo);
console.log("添加 Todo:", todo);
},
function delTodo(item) {
console.log("App.vue delTodo:", item);
todoStore.delTodo(item);
}
moveTodo({ oldIndex, newIndex }) {
const item = this.todos.splice(oldIndex, 1)[0];
this.todos.splice(newIndex, 0, item);
},
function moveTodo({ oldIndex, newIndex }) {
console.log("App.vue moveTodo:", oldIndex, newIndex);
todoStore.moveTodo(oldIndex, newIndex);
}
delTodo(item) {
console.log("删除 Todo:", item);
this.todos = this.todos.filter((t) => t.id !== item.id);
},
function editTodo({ item, text }) {
console.log("App.vue editTodo:", item, text);
todoStore.editTodo({ item, text });
}
editTodo({ item, text }) {
item.txt = text;
console.log("编辑 Todo:", item);
},
function changeTabType(type) {
console.log("App.vue tab change:", type);
todoStore.setTab(type);
}
toggleTodo(item) {
console.log("完成状态切换:", item);
},
changeTabType(type) {
console.log("切换 Tab:", type);
this.tabType = type;
},
},
watch: {
todos: {
handler(todos) {
console.log("Todos 改变:", todos);
localStorage.setItem("vue-todos", JSON.stringify(todos));
},
deep: true,
},
},
};
const showTodos = computed(() => {
if (todoStore.tabType === 1)
return todoStore.todos.filter((t) => !t.completed);
if (todoStore.tabType === 2)
return todoStore.todos.filter((t) => t.completed);
return todoStore.todos;
});
</script>
<template>
<TodoHeader @addTodo="addTodo" />
<TodoList :todos="todoList" @delTodo="delTodo" @move="moveTodo" @edit="editTodo"/>
<TodoList
:todos="showTodos"
@delTodo="delTodo"
@move="moveTodo"
@edit="editTodo"
/>
<TodoFooter
:count="todoList.length"
:tabType="tabType"
:count="showTodos.length"
:tabType="todoStore.tabType"
@changeTabType="changeTabType"
/>
</template>

View File

@@ -50,7 +50,7 @@ export default {
}
.active {
color: #409eff !important;
color: #ff4d4f !important;
font-weight: bold;
}
</style>

View File

@@ -16,6 +16,36 @@
</div>
</template>
<script>
const WORD_COUNT_LIMIT = 50;
export default {
data() {
return {
newTodo: "",
countLimit: WORD_COUNT_LIMIT,
};
},
computed: {
isShowMsg() {
return this.newTodo.length > WORD_COUNT_LIMIT;
},
},
methods: {
addNewTodo() {
if (this.newTodo.length === 0 || this.newTodo.length > WORD_COUNT_LIMIT) {
return;
}
this.$emit("addTodo", this.newTodo);
this.newTodo = "";
},
},
};
</script>
<style scoped>
h1 {
color: #409eff;
@@ -57,33 +87,3 @@ p {
text-align: center;
}
</style>
<script>
const WORD_COUNT_LIMIT = 50;
export default {
data() {
return {
newTodo: "",
countLimit: WORD_COUNT_LIMIT,
};
},
computed: {
isShowMsg() {
return this.newTodo.length > WORD_COUNT_LIMIT;
},
},
methods: {
addNewTodo() {
if (this.newTodo.length === 0 || this.newTodo.length > WORD_COUNT_LIMIT) {
return;
}
this.$emit("addTodo", this.newTodo);
this.newTodo = "";
},
},
};
</script>

View File

@@ -44,6 +44,49 @@
</div>
</template>
<script>
export default {
props: ["todos"],
data() {
return {
editingId: null,
editText: "",
dragIndex: null,
};
},
methods: {
delTodo(item) {
this.$emit("delTodo", item);
},
onDragStart(index) {
this.dragIndex = index;
},
onDragOver(index) {},
onDrop(index) {
if (this.dragIndex === null || this.dragIndex === index) return;
this.$emit("move", { oldIndex: this.dragIndex, newIndex: index });
this.dragIndex = null;
},
startEdit(item) {
this.editingId = item.id;
this.editText = item.txt;
},
confirmEdit(item) {
const text = this.editText.trim();
if (text) {
this.$emit("edit", { item, text });
}
this.editingId = null;
},
cancelEdit() {
this.editingId = null;
},
},
};
</script>
<style scoped>
.tdContainer {
margin-top: 20px;
@@ -125,45 +168,3 @@
box-sizing: border-box;
}
</style>
<script>
export default {
props: ["todos"],
data() {
return {
editingId: null,
editText: "",
};
},
methods: {
delTodo(item) {
this.$emit("delTodo", item);
},
onDragStart(index) {
this.dragIndex = index;
},
onDragOver(index) {},
onDrop(index) {
if (this.dragIndex === null || this.dragIndex === index) return;
this.$emit("move", { oldIndex: this.dragIndex, newIndex: index });
this.dragIndex = null;
},
startEdit(item) {
this.editingId = item.id;
this.editText = item.txt;
},
confirmEdit(item) {
const text = this.editText.trim();
if (text) {
this.$emit("edit", { item, text });
}
this.editingId = null;
},
cancelEdit() {
this.editingId = null;
},
},
};
</script>

View File

@@ -1,5 +1,6 @@
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import "./assets/global.css";
createApp(App).mount("#app");
createApp(App).use(createPinia()).mount("#app");

75
todos/src/stores/todo.js Normal file
View File

@@ -0,0 +1,75 @@
import { defineStore } from "pinia";
import { toRaw } from "vue";
const STORAGE_KEY = "vue-todos";
export const useTodoStore = defineStore("todoStore", {
state: () => ({
todos: JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]"),
tabType: 0, // 0全部 1未完成 2已完成
}),
getters: {
leftCount(state) {
return state.todos.filter((t) => !t.completed).length;
},
},
actions: {
save() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(toRaw(this.todos)));
},
setTab(type) {
this.tabType = type;
},
addTodo(txt) {
const newTodo = {
id: Date.now(),
txt,
completed: false,
};
this.todos.push(newTodo);
this.save();
console.log("Added:", newTodo);
console.log("Current todos:", toRaw(this.todos));
},
delTodo(item) {
this.todos = this.todos.filter((t) => t.id !== item.id);
this.save();
console.log("Deleted:", item);
console.log("Current todos:", toRaw(this.todos));
},
editTodo({ item, text }) {
const t = this.todos.find((t) => t.id === item.id);
if (t) {
t.txt = text;
this.save();
console.log("Edited:", item.id, "=>", text);
console.log("Current todos:", toRaw(this.todos));
}
},
moveTodo(oldIndex, newIndex) {
if (oldIndex === newIndex) return;
const arr = this.todos;
const moved = arr.splice(oldIndex, 1)[0];
arr.splice(newIndex, 0, moved);
this.save();
console.log(`Move: ${oldIndex}${newIndex}`);
console.log("Current todos:", toRaw(this.todos));
},
clearCompleted() {
this.todos = this.todos.filter((t) => !t.completed);
this.save();
console.log("Clear completed");
console.log("Current todos:", toRaw(this.todos));
},
},
});