[email protected]小目标列表

运行效果

在这里插入图片描述

源码

html 代码中需要插入css 代码,在最下方需插入vue 代码即可运行。

HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--插入css-->
</head>
<body>
<div id="app" class="main">
    <h2>小目标列表</h2>
    <div class="list">
        <h3>添加小目标</h3>
        <input type="text" class="text-keyword" placeholder="输入小目标后,按回车确认" @keyup.13='addList' v-model="addText"/>
        <!--如果noend等于0,就是全部完成了就显示‘全部完成了’,如果没有就是显示已完成多少条(prolist.length-noend)和未完成多少条(noend)-->
        <p>共有{
   
   {prolist.length}}个目标,{
   
   {noend==0?"全部完成了":'已完成'+(prolist.length-noend)+',还有'+noend+'条未完成'}}</p>
        <p>
            <input type="radio" name="chooseType" checked="true" @click='chooseList(1)'/><label>所有目标</label>
            <input type="radio" name="chooseType" @click='chooseList(2)'/><label>已完成目标</label>
            <input type="radio" name="chooseType" @click='chooseList(3)'/><label>未完成目标</label>
        </p>
    </div>
    <ul>
        <li class="li1" v-for="(list,index) in newList" :class="{
     
     'eidting':curIndex===index}">
            <div>
                <span class="status-span" @click="changeType(index)" :class="{
     
     'status-end':list.status}"></span>
                <span @dblclick="curIndex=index">{
   
   {list.name}}</span>
                <span class="close" @click='delectList(list)'>X</span>
            </div>
            <input type="text" class="text2" v-model='list.name' @keyup.esc='cancelEdit(list)' @blur='edited' @focus='editBefore(list.name)' @keyup.enter='edited' v-focus/>
        </li>
    </ul>
</div>
</body>
<!--插入js-->
</html>

CSS

<style>
body
{
    
    
	font-family:微软雅黑;
	font-size:14px;
}

input
{
    
    
	font-size:14px;
}

body,ul,div,html
{
    
    
	margin:0;
	padding:0;
}

.main
{
    
    
	width:800px;
	margin:0 auto;
}

li
{
    
    
	list-style-type:none;
	line-height:40px;
	position:relative;
	border:1px solid transparent;
	padding:0 20px;
}

li .status-span
{
    
    
	display:block;
	width:10px;
	height:10px;
	background:#ccc;
	float:left;
	margin:14px 10px 0 0;
}

li .status-span.status-end
{
    
    
	background:#09f;
}

li .close
{
    
    
	position:absolute;
	color:red;
	font-size:20px;
	line-height:40px;
	height:40px;
	right:20px;
	cursor:pointer;
	display:none;
	top:0;
}

li:hover
{
    
    
	border:1px solid #09f;
}

.text-keyword
{
    
    
	box-sizing:border-box;
	width:100%;
	height:40px;
	padding-left:10px;
	outline:none;
}

.hidden,li.eidting div
{
    
    
	display:none;
}

li:hover .close,li div,li.eidting .text2
{
    
    
	display:block;
}

li .text2,li .text-keyword
{
    
    
	height:40px;
	padding-left:10px;
	box-sizing:border-box;
	margin-left:10px;
	width:80%;
	display:none;
}
</style>

JS

<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script type="text/javascript">
new Vue({
    
    
    el: "#app",
    data: {
    
    
        addText:'',
        //name-名称,status-完成状态
        prolist:[
            {
    
    name:"HTML5",status:false},
            {
    
    name:"CSS3",status:false},
            {
    
    name:"vue",status:false},
            {
    
    name:"react",status:false}
        ],
        newList:[],
        curIndex:'',
        beforeEditText:"",
        curType:0
    },
    computed:{
    
    
        //计算属性,返回未完成目标的条数,就是数组里面status=false的条数
        noend:function(){
    
    
            return this.prolist.filter(function(item){
    
    
                return !item.status
            }).length;
        }
    },
    methods:{
    
    
        addList(){
    
    
            //添加进来默认status=false,就是未完成状态
            this.prolist.push({
    
    
                name:this.addText,
                status:false
            });
            //添加后,清空addText
            this.addText="";
        },
        chooseList(type){
    
    
            //type=1时,选择所有目标
            //type=2时,选择所有已完成目标
            //type=3时,选择所有未完成目标
            this.curType=type;
            switch(type){
    
    
                case 1:this.newList=this.prolist;break;
                case 2:this.newList=this.prolist.filter(function(item){
    
    return item.status});break;
                case 3:this.newList=this.prolist.filter(function(item){
    
    return !item.status});break;
            }
        },
        /*改变单条数据的完成状态*/
        changeType(index){
    
    
            this.newList[index].status=!this.newList[index].status;
            //更新数据
            this.chooseList(this.curType);
        },
        delectList(list){
    
    
            var index=this.prolist.indexOf(list);
            //根据索引,删除数组某一项
            this.prolist.splice(index,1);
            //更新newList  newList可能经过this.prolist.filter()赋值,这样的话,删除了prolist不会影响到newList  那么就要手动更新newList
            //this.newList=this.prolist;
            this.chooseList(this.curType);
        },
        //修改前
        editBefore(name){
    
    
            //先记录当前项(比如这一项,{name:"HTML5",status:false})
            //beforeEditText="HTML5"
            this.beforeEditText=name;
        },
        //修改完成后
        edited(){
    
    
            //修改完了,设置curIndex="",这样输入框就隐藏,其它元素就会显示。因为在li元素 写了::class="{'eidting':curIndex===index}"  当curIndex不等于index时,eidting类名就清除了!
            //输入框利用v-model绑定了当前项(比如这一项,{name:"HTML5",status:false})的name,当在输入框编辑的时候,比如改成‘HTML’,实际上当前项的name已经变成了‘HTML’,所以,这一步只是清除eidting类名,隐藏输入框而已
            //还有一个要注意的就是虽然li遍历的是newList,比如改了newList的这一项({name:"HTML5",status:false}),比如改成这样({name:"HTML",status:true})。实际上prolist的这一项({name:"HTML5",status:false}),也会被改成({name:"HTML",status:true})。因为这里是一个对象,而且公用一个堆栈!修改其中一个,另一个会被影响到
            this.curIndex="";
        },
        //取消修改
        cancelEdit(val){
    
    
            //上面说了输入框利用v-model绑定了当前项(比如这一项,{name:"HTML5",status:false})的name,当在输入框编辑的时候,比如改成‘HTML’,实际上当前项的name已经变成了‘HTML’,所以,这一步就是把之前保存的beforeEditText赋值给当前项的name属性,起到一个恢复原来值得作用!
            val.name=this.beforeEditText;
            this.curIndex="";
        }
    },
    mounted(){
    
    
        //初始化,把prolist赋值给newList。默认显示所有目标
        this.newList=this.prolist;
    },
    directives:{
    
    
        "focus":{
    
    
            update(el){
    
    
                el.focus();
            }
        }
    }
});
</script>

原理分析和实现

首先,还是先把布局写好,和引入vue,准备vue实例,这个不多说,代码如下。

步骤 1 布局和引入 vue

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
body {
      
      
    font-family: "微软雅黑";
    font-size: 14px;
}

input {
      
      
    font-size: 14px;
}

body,
ul,
div,
html {
      
      
    padding: 0;
    margin: 0;
}

.hidden {
      
      
    display: none;
}

.main {
      
      
    width: 800px;
    margin: 0 auto;
}

li {
      
      
    list-style-type: none;
    line-height: 40px;
    position: relative;
    border: 1px solid transparent;
    padding: 0 20px;
}

li .type-span {
      
      
    display: block;
    width: 10px;
    height: 10px;
    background: #ccc;
    margin: 14px 10px 0 0;
    float: left;
}

li .close {
      
      
    position: absolute;
    color: #f00;
    font-size: 20px;
    line-height: 40px;
    height: 40px;
    right: 20px;
    cursor: pointer;
    display: none;
    top: 0;
}

li:hover {
      
      
    border: 1px solid #09f;
}

li:hover .close {
      
      
    display: block;
}

li .text-keyword {
      
      
    height: 40px;
    padding-left: 10px;
    box-sizing: border-box;
    margin-left: 10px;
    width: 80%;
    display: none;
}

.text-keyword {
      
      
    box-sizing: border-box;
    width: 100%;
    height: 40px;
    padding-left: 10px;
    outline: none;
}
</style>
</head>
<body>

<div id="app" class="main">
    <h2>小目标列表</h2>
    <div class="list">
        <h3>添加小目标</h3>
        <input type="text" class="text-keyword" placeholder="输入小目标后,按回车确认" />
        <p>共有N个目标</p>
        <p>
            <input type="radio" name="chooseType" checked="true" /><label>所有目标</label>
            <input type="radio" name="chooseType" /><label>已完成目标</label>
            <input type="radio" name="chooseType" /><label>未完成目标</label>
        </p>
    </div>
    <ul>
        <li class="li1">
            <div>
                <span class="type-span"></span>
                <span>html5</span>
                <span class="close">X</span>
            </div>
        </li>
        <li class="li1">
            <div>
                <span class="type-span"></span>
                <span>css3</span>
                <span class="close">X</span>
            </div>
        </li>
    </ul>
</div>

</body>
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script type="text/javascript">
new Vue({
      
      
    el: "#app",
    data: {
      
      },
    computed: {
      
      },
    methods: {
      
      }
});
</script>
</html>

布局有了,相当于一个骨架就有了,下面实现功能,一个一个来。

步骤 2 输入框回车要添加纪录

输入并回车,多一条记录,下面的记录文字也会改变。

在这里插入图片描述
首先,大的输入框回车要添加纪录,那么输入框必须绑定一个值和一个添加纪录的方法。
在这里插入图片描述

然后,下面的记录也要改变,所以,下面的记录也要帮一个值,因为这个记录可能会有多个,这个值就是一个数组,也可以看到,记录除了名称,还有记录是否完成的状态,所以,绑定记录的这个值肯定是一个对象数组!

为了着重表示我修改了什么地方,代码我现在只贴出修改的部分,大家对着上面的布局,就很容易知道我改的是什么地方了!

<!--利用v-model把addText绑定到input-->
<input type="text" 
	   class="text-keyword" 
	   placeholder="输入小目标后,按回车确认" 
	   @keyup.13='addList' v-model="addText"/>

<p>共有{
   
   {prolist.length}}个目标</p>

<!--v-for遍历prolist-->
<li class="li1" v-for="list in prolist">
    <div>
        <span class="type-span"></span>
        <span>{
   
   {list.name}}</span>
        <span class="close">X</span>
    </div>
</li>
new Vue({
    
    
    el: "#app",
    data: {
    
    
       addText:'',
       //name-名称,status-完成状态
       prolist:[
               {
    
    name:"HTML5",status:false},
               {
    
    name:"CSS3",status:false},
               {
    
    name:"vue",status:false},
               {
    
    name:"react",status:false}
        ]
    },
    computed:{
    
    
        
    },
    methods:{
    
    
        addList(){
    
    
            //添加进来默认status=false,就是未完成状态
            this.prolist.push({
    
    
                name:this.addText,
                status:false
            });
            //添加后,清空addText
            this.addText="";
        }
    }
});

测试一下,没问题
在这里插入图片描述

步骤 3 点击切换,下面记录会改变

在这里插入图片描述
看到三个选项,也很简单,无非就是三个选择,一个是所有的目标,一个是所有已经完成的目标,一个是所有没完成的目标。

首先,新建一个新的变量(newList),储存 prolist。遍历的时候不再遍历 prolist,而是遍历 newList。改变也是改变 newList。

然后选择所有目标的时候,显示全部 prolist,把 prolist 赋值给 newList。

然后选择所有已经完成目标的时候,只显示 prolist 中,status 为 true 的目标,把 prolist中,status 为 true 的项赋值给 newList,最后.选择所有未完成目标的时候,只显示 status 为 false 的目标,把 prolist 中,status 为 false 的项赋值给 newList。

 <ul>
    <li class="li1" v-for="list in newList">
        <div>
            <span class="status-span"></span>
            <span>{
   
   {list.name}}</span>
            <span class="close" @click='delectList(index)'>X</span>
        </div>
    </li>
</ul>
new Vue({
    
    
    el: "#app",
    data: {
    
    
        addText:'',
        //name-名称,status-完成状态
       prolist:[
               {
    
    name:"HTML5",status:false},
               {
    
    name:"CSS3",status:false},
               {
    
    name:"vue",status:false},
               {
    
    name:"react",status:false}
        ],
        newList:[]
    },
    computed:{
    
    
        noend:function(){
    
    
            return this.prolist.filter(function(item){
    
    
                return !item.status
            }).length;
        }
    },
    methods:{
    
    
        addList(){
    
    
            //添加进来默认status=false,就是未完成状态
            this.prolist.push({
    
    
                name:this.addText,
                status:false
            });
            //添加后,清空addText
            this.addText="";
        },
        chooseList(type){
    
    
            //type=1时,选择所有目标
            //type=2时,选择所有已完成目标
            //type=3时,选择所有未完成目标
            switch(type){
    
    
                case 1:this.newList=this.prolist;break;
                case 2:this.newList=this.prolist.filter(function(item){
    
    return item.status});break;
                case 3:this.newList=this.prolist.filter(function(item){
    
    return !item.status});break;
            }
        },
        delectList(index){
    
    
            //根据索引,删除数组某一项
            this.prolist.splice(index,1);
            //更新newList  newList可能经过this.prolist.filter()赋值,这样的话,删除了prolist不会影响到newList  那么就要手动更新newList
            this.newList=this.prolist;
        },
    },
    mounted(){
    
    
        //初始化,把prolist赋值给newList。默认显示所有目标
        this.newList=this.prolist;
    }
});

运行结果
在这里插入图片描述

步骤 4 红色关闭标识,点击会删除该条记录

红色关闭标识,点击会删除该记录。前面按钮点击会切换该记录完成状态,颜色也改变,记录文字也跟着改变。

在这里插入图片描述
首先点击红色关闭标识,点击会删除该记录。
这个应该没什么问题,就是删除 prolist 的一条记录!

然后前面按钮点击会切换该记录完成状态。
这个也没什么,就是改变 prolist 的一条记录的 status 字段!

最后记录文字的改变,就是记录 prolist 中 status 为 false 的有多少条,prolist 中 status 为true 的有多少条而已。

<!--
如果 noend 等于0,就是全部完成了就显示‘全部完成了’,
如果没有就是显示已完成多少条(prolist.length-noend)和未完成多少条(noend)-->
<p>
共有{
   
   {prolist.length}}个目标,
{
   
   {noend==0?"全部完成了":'已完成'+(prolist.length-noend)+',
还有'+noend+'条未完成'}}
</p>

<ul>
    <li class="li1" v-for="(list,index) in newList">
        <div>
            <span class="status-span" 
            	  @click="list.status=!list.status" 
            	  :class="{
     
     'status-end':list.status}">
            </span>
            <span>{
   
   {list.name}}</span>
            <span class="close" @click='delectList(index)'>X</span>
        </div>
    </li>
</ul>
new Vue({
    
    
    el: "#app",
    data: {
    
    
        addText:'',
        //name-名称,status-完成状态
       prolist:[
               {
    
    name:"HTML5",status:false},
               {
    
    name:"CSS3",status:false},
               {
    
    name:"vue",status:false},
               {
    
    name:"react",status:false}
        ],
        newList:[]
    },
    computed:{
    
    
        //计算属性,返回未完成目标的条数,就是数组里面status=false的条数
        noend:function(){
    
    
            return this.prolist.filter(function(item){
    
    
                return !item.status
            }).length;
        }
    },
    methods:{
    
    
        addList(){
    
    
            //添加进来默认status=false,就是未完成状态
            this.prolist.push({
    
    
                name:this.addText,
                status:false
            });
            //添加后,清空addText
            this.addText="";
        },
        chooseList(type){
    
    
            switch(type){
    
    
                case 1:this.newList=this.prolist;break;
                case 2:this.newList=this.prolist.filter(function(item){
    
    return item.status});break;
                case 3:this.newList=this.prolist.filter(function(item){
    
    return !item.status});break;
            }
        },
        delectList(index){
    
    
            //根据索引,删除数组某一项
            this.prolist.splice(index,1);
            //更新newList  newList可能经过this.prolist.filter()赋值,这样的话,删除了prolist不会影响到newList  那么就要手动更新newList
            this.newList=this.prolist;
        },
    },
    mounted(){
    
    
        this.newList=this.prolist;
    }
});

运行结果
在这里插入图片描述

步骤 5 文字双击会出现输入框,可输入文字

文字双击会出现输入框,可输入文字,如果回车或者失去焦点,就改变文字,如果按下ESC就恢复原来的文字。
在这里插入图片描述
首先.双击出现输入框,就是双击文字后,给当前的li设置一个类名(‘eidting’),然后写好样式。当li出现这个类名的时候,就出现输入框,并且隐藏其它内容。

然后回车或者失去焦点,就改变文字这个只需要操作一个,就是把类名(‘eidting’)清除掉。然后输入框就会隐藏,其它内容显示!

最后.按下ESC就恢复原来的文字,就是出现输入框的时候,用一个变量(‘beforeEditText’)先保存当前的内容,然后按下了ESC,就把变量(‘beforeEditText’)赋值给当前操作的值!

<ul>
    <li class="li1" 
    	v-for="(list,index) in newList" 
    	:class="{
     
     'eidting':curIndex===index}">
    	
        <div>
            <span class="status-span" @click="list.status=!list.status" :class="{
     
     'status-end':list.status}"></span>
            <span @dblclick="curIndex=index">{
   
   {list.name}}</span>
            <span class="close" @click='delectList(index)'>X</span>
        </div>
        
        <input type="text" 
        	   class="text2" 
        	   v-model='list.name' 
        	   @keyup.esc='cancelEdit(list)' 
        	   @blur='edited' 
        	   @focus='editBefore(list.name)' 
        	   @keyup.enter='edited'/>
    </li>
</ul>
li div{
    
    display: block;}
li.eidting div{
    
    display: none;}
li .text2{
    
    height: 40px;padding-left: 10px;box-sizing: border-box;margin-left: 10px;width: 80%;display: none;}
li.eidting .text2{
    
    display: block;}
methods:{
    
    
    addList(){
    
    
        //添加进来默认status=false,就是未完成状态
        this.prolist.push({
    
    
            name:this.addText,
            status:false
        });
        //添加后,清空addText
        this.addText="";
    },
    chooseList(type){
    
    
        //type=1时,选择所有目标
        //type=2时,选择所有已完成目标
        //type=3时,选择所有未完成目标
        switch(type){
    
    
            case 1:this.newList=this.prolist;break;
            case 2:this.newList=this.prolist.filter(function(item){
    
    return item.status});break;
            case 3:this.newList=this.prolist.filter(function(item){
    
    return !item.status});break;
        }
    },
    delectList(index){
    
    
        //根据索引,删除数组某一项
        this.prolist.splice(index,1);
        //更新newList  newList可能经过this.prolist.filter()赋值,这样的话,删除了prolist不会影响到newList  那么就要手动更新newList
        this.newList=this.prolist;
    },
    //修改前
    editBefore(name){
    
    
        //先记录当前项(比如这一项,{name:"HTML5",status:false})
        //beforeEditText="HTML5"
        this.beforeEditText=name;
    },
    //修改完成后
    edited(){
    
    
        //修改完了,设置curIndex="",这样输入框就隐藏,其它元素就会显示。因为在li元素 写了::class="{'eidting':curIndex===index}"  当curIndex不等于index时,eidting类名就清除了!
        //输入框利用v-model绑定了当前项(比如这一项,{name:"HTML5",status:false})的name,当在输入框编辑的时候,比如改成‘HTML’,实际上当前项的name已经变成了‘HTML’,所以,这一步只是清除eidting类名,隐藏输入框而已
        //还有一个要注意的就是虽然li遍历的是newList,比如改了newList的这一项({name:"HTML5",status:false}),比如改成这样({name:"HTML",status:true})。实际上prolist的这一项({name:"HTML5",status:false}),也会被改成({name:"HTML",status:true})。因为这里是一个对象,而且公用一个堆栈!修改其中一个,另一个会被影响到
        this.curIndex="";
    },
    //取消修改
    cancelEdit(val){
    
    
        //上面说了输入框利用v-model绑定了当前项(比如这一项,{name:"HTML5",status:false})的name,当在输入框编辑的时候,比如改成‘HTML’,实际上当前项的name已经变成了‘HTML’,所以,这一步就是把之前保存的beforeEditText赋值给当前项的name属性,起到一个恢复原来值得作用!
        val.name=this.beforeEditText;
        this.curIndex="";
    }
},

运行结果
在这里插入图片描述
还有一个小细节,大家可能注意到了,就是双击文字,出来输入框的时候,还要自己手动点击一下,才能获得焦点,我们想双击了,输入框出来的时候,自动获取焦点,怎么办?

自定义指令就行了!

computed:{
    
    ...},
methods:{
    
    ...},
mounted(){
    
    ...},
directives:{
    
    
    "focus":{
    
    
        update(el){
    
    
            el.focus();
        }
    }
}
<input type="text" 
	   class="text2" 
	   v-model='list.name' 
	   @keyup.esc='cancelEdit(list)' 
	   @blur='edited' 
	   @focus='editBefore(list.name)' 
	   @keyup.enter='edited' v-focus/>

猜你喜欢

转载自blog.csdn.net/weiguang102/article/details/124348435