一、创建Vue项目
创建项目的方式有很多,这就简单的说两种吧:
1、命令行创建
win+r 输入cmd 回车 cd进入你要创建的目录
输入下面的命令,遇到需要确认的地方按回车就行了,一般没有什么问题
vue init webpack todolist // todolist是项目名称
创建成功以后
进入项目里面安装依赖运行
cd todolist
npm install // yarn 或 cnpm install
npm run dev
2、如果使用的HBuilder开发工具,可以直接在工具中创建项目
点击左上角的文件->新建->项目
输入名称,选好文件夹,点击创建就可以了
项目创建完成后
1、在src目录下新建pages文件夹,里面创建todolist.vue文件
2、配置路由:
1)安装 npm i router
2)在src目录下新建router文件夹,再创建一个index.js文件用来配置路由
import Vue from "vue"
import Router from "vue-router"
Vue.use(Router)
import Index from '@/pages/todoist'
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'index',
component: Index
},
]
})
export default router
3)main.js文件中引入路由
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import VueRouter from 'vue-router'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
router,
render: h => h(App)
}).$mount('#app')
最终
项目文件如下:
二、组件
todolist可以分为三个板块
1、头部(添加输入栏目)
2、列表(添加栏目列表)
3、底部(按钮等)
我们可以将这三个板块拆分成三个组件:
1、在todolist文件夹下新建components文件夹
2、在文件夹下新建三个组件:
todoHeader, todoItem, todoFooter
3、引入组件
import todoHeader from './components/todoHeader.vue'
import todoItem from './components/todoItem.vue'
import todoFooter from './components/todoFooter.vue'
export default {
components: {
todoHeader,
todoItem,
todoFooter
},
data() {
return {}
},
methods: {}
}
三、组件代码
index.vue
<template>
<div class="page flex">
<div class="card">
<h1>Todo List</h1>
<todoHeader @confirm="confirm" />
<todoItem :list="list" @change="changeList" />
<todoFooter :list="list" @change="allChange" @delete="show_confirm" />
</div>
</div>
</template>
<script>
import todoHeader from './components/todoHeader.vue'
import todoItem from './components/todoItem.vue'
import todoFooter from './components/todoFooter.vue'
export default {
components: {
todoHeader,
todoItem,
todoFooter
},
data() {
return {
list: [
// {
// value: '吃饭',
// checked: false
// },
// {
// value: '睡觉',
// checked: false
// }
]
}
},
methods: {
changeList({checked, index}) {
this.list[index].checked = checked
},
confirm(value) {
this.list.push({
value,
checked: false
})
},
allChange(checked) {
this.list.map(item => {
item.checked = checked
})
},
show_confirm() {
if (this.list.length > 0) {
let flag = false
this.list.map(item => {
if (item.checked) {
flag = true
}
})
if (flag) {
let modal = confirm("确定要删除已经完成的任务吗?");
if (modal) {
this.del()
}
} else {
alert('只能删除已经完成的任务!')
}
} else {
alert('没有可删除的任务!')
}
},
del() {
let arr = []
this.list.map((item, index) => {
if (!item.checked) {
arr.push(item)
}
})
this.list = arr
}
}
}
</script>
<style>
.page{
width: 100%;
margin-top: 50px;
justify-content: center;
}
.flex{
display: flex;
align-items: center;
}
.flex-1{
flex: 1;
}
.card{
width: 500px;
padding: 15px 16px;
border-radius: 4px;
border: 1px solid #DCDFE6;
}
.card:hover{
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)
}
h1{
text-align: center;
margin-bottom: 20px;
}
</style>
todoHeader.vue
<template>
<div class="header flex">
<div class="inputs flex-1" :class="isfocus ? 'focus' :''">
<input placeholder="请输入你的任务名称,按回车键确定" class="input" type="text" v-model="value" @focus="focus" @blur="blur" @keyup.enter="confirm" />
</div>
<button v-show="isfocus" class="btn" type="button">Add</button>
</div>
</template>
<script>
export default {
data() {
return {
isfocus: false,
value: ''
}
},
methods: {
focus() {
this.isfocus = true
},
blur() {
this.isfocus = false
},
confirm() {
this.$emit('confirm', this.value)
this.value = ''
}
}
}
</script>
<style>
.header{
width: 100%;
margin-bottom: 20px;
justify-content: space-between;
}
.inputs{
border-radius: 4px;
border: 1px solid #DCDFE6;
padding: 6px 10px;
}
.inputs.focus{
border: 1px solid #409EFF;
box-shadow: 0 1px 8px 0 rgba(64, 158, 255, 0.4)
}
.input{
width: 100%;
height: 20px;
}
.btn{
padding: 6px 10px;
background-color: red;
color: #FFFFFF;
border-radius: 4px;
cursor: pointer;
margin-left: 20px;
}
.btn:hover{
background-color: #F56C6C;
}
</style>
todoItem.vue
<template>
<div class="list">
<ul v-if="list.length">
<li class="flex" v-for="(item, index) in list" :key="index">
<div class="flex-1" @click="change(!item.checked, index)">
<input class="checkbox" type="checkbox" :value="item.value" v-model="item.checked" @change.stop="change(item.checked, index)" />
<label class="label">{{ item.value }}</label>
</div>
<div v-if="item.checked" style="color: #C0C4CC;">已完成</div>
</li>
</ul>
<div class="empty flex" v-else>
<span>暂无任务</span>
</div>
</div>
</template>
<script>
export default {
props: {
list: {
type: Array,
default: []
}
},
data() {
return {}
},
methods: {
change(checked, index) {
this.$emit('change', {checked, index})
}
}
}
</script>
<style>
.list{
width: 100%;
margin-bottom: 20px;
}
ul{
width: 100%;
border-radius: 4px;
border: 1px solid #DCDFE6;
}
li{
padding: 0 10px;
border-bottom: 1px solid #DCDFE6;
justify-content: space-between;
cursor: pointer;
}
li > div{
padding: 8px 0;
}
li > div > .label{
margin-left: 8px;
font-size: 14px;
line-height: 14px;
}
li:last-child{
border-bottom: none;
}
li:hover{
color: #409eff;
background-color: #ecf5ff;
}
.empty{
justify-content: center;
width: 100%;
height: 50px;
}
.empty span{
color: #C0C4CC;
font-size: 20px;
}
</style>
todoFooter.vue
<template>
<div class="footer flex">
<div class="flex">
<input class="checkbox" type="checkbox" value="all" v-model="checked" @change="change(checked)" />
<label class="label">
已完成 ({{ count }})
/
全部 ({{ total }})
</label>
</div>
<div>
<button class="btn" type="button" @click="del">Delete</button>
</div>
</div>
</template>
<script>
export default {
props: {
list: {
type: Array,
default: []
}
},
data() {
return {
checked: false
}
},
computed: {
total() {
return this.list.length
},
count() {
let count = 0
this.list.map(item => {
if (item.checked) {
count += 1
}
})
return count
}
},
methods: {
change(checked) {
this.$emit('change', checked)
},
del() {
this.$emit('delete')
}
}
}
</script>
<style>
.footer{
width: 100%;
justify-content: space-between;
}
.btn{
padding: 6px 10px;
background-color: red;
color: #FFFFFF;
border-radius: 4px;
cursor: pointer;
}
.btn:hover{
background-color: #F56C6C;
}
.label{
margin-left: 15px;
line-height: 14px;
font-size: 14px;
}
</style>
组件通信
1、父子组件之间往往是需要进行数据交互的,他们之间的数据传递的方法也有很多,这里使用的是props的方法
props: {
list: { // 传递的数据名称
type: Array, // 数据类型
default: [] // 默认值
}
}
2、在子组件中想要调用父组件的方法这里是采用的$emit
// 子组件中
change(checked, index) {
this.$emit('change', {checked, index}) // 第一个参数是传递的方法名称, 第二个参数是需要项外传递的参数
}
// 父组件中
// 通过子组件中定义的change来接收执行方法
<todoItem :list="list" @change="changeList" />
图片演示