我正在用Vue (v2.x.x)开发一个小型的待办事项应用程序。我最初列出了来自jsonplaceholder的5项待办事项。
我可以添加更多的待办事项,删除他们或标记他们做了。
var app = new Vue({
el: "#toDoApp",
data: {
url: "https://jsonplaceholder.typicode.com/todos?&_limit=5",
dataIsLoaded: false,
isValidInput: true,
todos: [],
unsolvedTodos: [],
newTitle: "",
},
mounted() {
axios.get(this.url)
.then((response) => {
this.todos = response.data;
})
.then(this.getUnsolvedTodos);
this.dataIsLoaded = true;
},
methods: {
getUnsolvedTodos: function() {
this.unsolvedTodos = this.todos.filter(todo => {
return todo.completed === false;
});
},
// toggle todo
toggleTodo: function(todo) {
todo.completed = !todo.completed;
// Update unsolved count
this.getUnsolvedTodos();
},
// delete todo
deleteTodo: function(id) {
this.todos = this.todos.filter(todo => todo.id !== id);
// Update unsolved count
this.getUnsolvedTodos();
},
// validate todo title
validateInput: function() {
this.isValidInput = this.newTitle.length >= 3 ? true : false;
},
// add todo
addTodo: function() {
let lastId = this.todos.length === 0 ? 0 : this.todos[this.todos.length - 1].id;
const newToDo = {
id: lastId + 1,
title: this.newTitle,
completed: false
}
this.validateInput();
if (this.isValidInput) {
this.todos.push(newToDo);
this.newTitle = "";
}
// Update unsolved count
this.getUnsolvedTodos();
}
},
created() {},
watch: {}
});
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
.app-wrapper {
height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #0093e9;
background-image: linear-gradient(160deg, #0093e9 0%, #80d0c7 100%);
}
#toDoApp {
width: 320px;
height: 320px;
display: flex;
flex-direction: column;
background: #fff;
box-shadow: rgb(99 99 99 / 20%) 0px 2px 8px 0px;
overflow: hidden;
border-radius: 8px;
position: relative;
}
header {
padding: 15px 20px;
background: #efefef;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
font-size: 18px;
font-weight: 600;
display: flex;
}
header .title {
font-size: 18px;
font-weight: 600;
color: #323232;
}
header .count {
margin-left: auto;
display: inline-block;
width: 26px;
line-height: 26px;
text-align: center;
border-radius: 3px;
font-size: 13px;
background: #c00;
color: #fff;
}
header .count.zero {
background: #009688;
}
.todo-list {
flex: 1;
overflow-y: auto;
list-style-type: none;
padding: 15px 20px 5px 20px;
color: #4f4f4f;
}
::-webkit-scrollbar {
width: 5px;
}
/* Track */
::-webkit-scrollbar-track {
background: #efefef;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #d0d0d0;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #d0d0d0;
}
.todo-list li {
position: relative;
display: flex;
overflow: hidden;
align-items: center;
font-size: 13px;
padding: 10px;
cursor: pointer;
margin-bottom: 7px;
border: 1px solid rgba(0, 0, 0, 0.07);
background: #d4e7f7;
border-radius: 2px;
}
.todo-list li:last-child {
margin-bottom: 0;
}
.done {
text-decoration: line-through;
}
input[type="checkbox"] {
display: block;
margin-bottom: -1px;
margin-right: 7px;
}
button {
border: none;
background: #c00;
color: #fff;
cursor: pointer;
position: absolute;
z-index: 1;
top: -1px;
bottom: -1px;
right: -1px;
font-size: 18px;
width: 50px;
transform: translateX(50px);
transition: transform 200ms;
}
.todo-list li:hover button {
transform: translateX(0);
}
footer {
padding: 15px 20px 25px 20px;
margin-top: auto;
position: relative;
}
.error {
position: absolute;
top: 5px;
right: 22px;
font-size: 11px;
color: #c00;
}
input[type="text"] {
width: 100%;
padding: 2px 4px;
border: none;
font-size: 16px;
outline: none;
border-bottom: 2px solid #b8b8b8;
transition: border-bottom 200ms ease-in;
}
input[type="text"]:focus {
border-bottom: 2px solid #dddddd;
}
.loader {
border: 4px solid #ccc;
border-top-color: transparent;
border-radius: 50%;
width: 50px;
height: 50px;
position: absolute;
top: 50%;
margin-left: -25px;
left: 50%;
margin-top: -25px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<div class="app-wrapper">
<div id="toDoApp">
<header>
<span class="title">My todo list</span>
<span class="count" v-bind:class="{zero: unsolvedTodos.length === 0}">{{unsolvedTodos.length}}</span>
</header>
<ul class="todo-list" v-if="dataIsLoaded">
<li v-for="todo in todos.slice().reverse()" v-bind:class="{done: todo.completed}" :id="todo.id">
<input type="checkbox" :checked="todo.completed" @change="toggleTodo(todo)" />
<span class="title">{{todo.title}}</span>
<button @click="deleteTodo(todo.id)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</li>
</ul>
<div class="loader" v-else></div>
<footer>
<form @submit.prevent="addTodo()">
<input type="text" placeholder="Add new todo..." v-model="newTitle">
<span class="error" v-if="!isValidInput">Please add at least 3 characters</span>
</form>
</footer>
</div>
</div>
发布于 2021-05-18 09:47:33
你是以一种非常程序化的方式做事情的,比如你如何改变TODO-项目,然后计算未解决的TODOs的数量。在使用Vue时,有一种更好的方法,即使用计算性质
methods: {
toggleTodo: function(todo) {
todo.completed = !todo.completed;
},
deleteTodo: function(id) {
this.todos = this.todos.filter(todo => todo.id !== id);
},
},
computed: {
unsolvedTodos: function() {
return this.todos.filter(todo => !todo.completed);
},
validInput: function() {
return this.newTitle.length >= 3;
}
},
体系结构容易理解吗?
是的,但这并不是最理想的,因为我上面提到过。
除了Axios,还有更好的选择吗?
不,在我看来不是。坚持Axios。
https://codereview.stackexchange.com/questions/260898
复制相似问题