前端vue入门(纯代码)07

07.TodoList案例

在这里插入图片描述

1 拆分组件

一共拆分为4个组件:【因为header和HTML内置的标签<header>重名,故在之前都加了Todo】

  • TodoHeader
  • TodoItem【item是list的子组件】
  • TodoList
  • TodoFooter
2 组件化编码流程
  1. 实现静态组件:抽取组件,只考虑结构和样式,先拆结构再拆样式。

整个TodoList案例的页面结构【html】和样式设置【style】:

TodoList.html文件【还未实现动态交互】

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      background: #fff;
    }

    .todo-container {
      width: 600px;
      /* 上下外边距为0,左右自动,实际效果为左右居中*/
      margin: 0 auto;
    }

    /* 后代选择器(包含选择器),选择到的是todo-container下面的所有后代todo-wrap  */
    .todo-container .todo-wrap {
      padding: 10px;
      border: 1px solid #67dbd1;
      border-radius: 5px;
    }

    /* 头部样式设置 */
    /* 后代选择器(包含选择器),选择到的是todo-header下面的所有后代input */
    .todo-header input {
		width: 560px;
		height: 28px;
		font-size: 14px;
		border: 1px solid #ccc;
		border-radius: 4px;
    /* 内边距:上下4px,左右7px */
		padding: 4px 7px;
	}

  /* :focus获得焦点,并设置其新样式:例如:用户单击一个input输入框获取焦点,然后这个input输入框的边框样式就会发生改变,和其他的输入框区别开来,表明已被选中。 */
	.todo-header input:focus {
    /* outline 与 border 相似,不同之处在于 outline 在整个元素周围画了一条线;它不能像 border 那样,指定在元素的一个面上设置轮廓,也就是不能单独设置顶部轮廓、右侧轮廓、底部轮廓或左侧轮廓。 */
		outline: none;
    /* 定义边框的颜色 */
		border-color: rgba(82, 168, 236, 0.8);
    /* boxShadow 属性把一个或多个下拉阴影添加到框上 */
    /* 设置inset:内部阴影,不设置inset:外部阴影 */
    /* 【0 0】:不设置X轴与Y轴偏移量 */
    /* 第三个值【如10px,8px】:设置值阴影模糊半径为15px */
		box-shadow: inset 0 0 10px rgba(124, 56, 207, 0.075), 0 0 8px rgba(224, 58, 17, 0.6);
    background-color: bisque;
	}

	/*main*/
	.todo-main {
    /* 左外边距:0px 【盒子贴着盒子】*/
		margin-left: 0px;
		border: 1px solid #ddd;
		border-radius: 2px;
		padding: 0px;
	}

	.todo-empty {
		height: 40px;
		line-height: 40px;
		border: 1px solid #ddd;
		border-radius: 2px;
		padding-left: 5px;
		margin-top: 10px;
	}

  /*item*/
	li {
    /* ul无序列表 ol有序列表*/
    /* 列表前面无标记 */
		list-style: none;
    /* height定义了一个li盒子的高度 */
		height: 36px;
    /* 行高:指的是文字占有的实际高度 */
		line-height: 36px;
    /* 当height和line-height相等时,即盒子的高度和行高一样,内容上下居中 */
		padding: 0 5px;
    /* 边框底部:1px的实心线 颜色*/
		border-bottom: 1px solid #c0abc3;
	}

  /* 后代选择器(包含选择器),选择到的是li下面的所有后代label */
	li label {
    /* 左对齐浮动【元素一旦浮动就会脱离文档流(不占位,漂浮)】 */
		float: left;
    /* 鼠标放在label元素上时变成小小手 */
		cursor: pointer;
	}

	li label input {
    /* 垂直居中 */
		vertical-align: middle;
		margin-right: 6px;
    /* 定位 */
		position: relative;
		top: -1px;
	}

   /* 后代选择器(包含选择器),选择到的是li下面的所有后代button */
	li button {
    /* 向右浮动 */
		float: right;
    /* 不为被隐藏的对象保留其物理空间,即该对象在页面上彻底消失,通俗来说就是看不见也摸不到。 隐藏,并没有删掉该元素*/
		display: none;
    /* 上边距为3px */
		margin-top: 3px;
	}

  /* :before 选择器向选定的每个li元素前插入内容 */
	li:before {
    /* initial:它将属性设置为其默认值。 */
		content: initial;
	}

   /* 结构伪类选择器 选择最后一个li元素 */ 
	li:last-child {
    /* 边框底部没有线 */
		border-bottom: none;
	}
 /* 鼠标移动到该元素上时,li元素背景变色 */
	li:hover{
		background-color: #ddd;
	}
	
  /* 鼠标移动到该元素上时,将button按钮显示出来 */
	li:hover button{
    /*  display:block将元素显示为块级元素 */
		display: block;
	}

/* 尾部 */

  .todo-footer {
		height: 40px;
		line-height: 40px;
		padding-left: 6px;
		margin-top: 5px;
	}

  /* 后代选择器:todo-footer里所有的label元素 */
	.todo-footer label {
    /* inline元素不会独占一行,多个相邻的行内元素会排列在同一行里,直到一行排列不下,才会新换一行,其宽度随元素的内容而变化 */
    /* inline-block 的元素既具有 block 元素可以设置宽高的特性,同时又具有 inline 元素默认不换行的特性 */
		display: inline-block;
		margin-right: 20px;
		cursor: pointer;
	}

  .btn {
    /* 行内的块级元素 */
		display: inline-block;
		padding: 4px 12px;
		margin-bottom: 0;
		font-size: 14px;
		line-height: 20px;
    /* 文本内容居中 */
		text-align: center;
    /* 垂直居中 */
		vertical-align: middle;
		cursor: pointer;
		box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
		border-radius: 4px;
	}
	.btn-danger {
    /* 字体颜色设置:白色 */
		color: #fff;
		background-color: #da4f49;
		border: 1px solid #bd362f;
	}
  /* 鼠标移动到删除按钮时 */
	.btn-danger:hover {
		color: #fff;
		background-color: #bd362f;
	}
	.btn:focus {
		outline: none;
	}

	.todo-footer label input {
		position: relative;
		top: -1px;
		vertical-align: middle;
		margin-right: 5px;
	}

	.todo-footer button {
		float: right;
		margin-top: 5px;
	}
  </style>
</head>

<body>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <!-- 头部 -->
        <div class="todo-header">
          <input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
        </div>
        <ul class="todo-main">
          <li>
            <label>
              <input type="checkbox"/>
              <span>练功夫</span>
            </label>
            <button class="btn btn-danger">删除</button>
          </li>
          <li>
            <label>
              <input type="checkbox"/>
              <span>睡觉</span>
            </label>
            <button class="btn btn-danger">删除</button>
          </li>          
          <li>
            <label>
              <input type="checkbox"/>
              <span>打豆豆</span>
            </label>
            <button class="btn btn-danger">删除</button>
          </li>
        </ul>
        <!-- 尾部 -->
        <div class="todo-footer">
          <label>
            <input type="checkbox"/>
          </label>
          <span>
            <span>已完成:0</span> / 全部:3
          </span>
          <button class="btn btn-danger">清除已完成任务</button>
        </div>
      </div>
    </div>
  </div>
</body>

</html>

静态页面展示:

在这里插入图片描述

  1. 展示动态数据

前期知识准备:

  • <input type="checkbox" :checked="false"/> 【false:input框不勾选】

  • <input type="checkbox" :checked="true"/>【true:input框勾选】

  • <input type="text" @keyup.enter="add"/>【键盘按键事件:按下回车按键,调用add()方法】

  • event.target是会返回触发事件触发的源头元素。【这个源头元素指的是,当我点击子元素,虽然父元素的点击事件也会被触发(冒泡机制),但子元素才是事件的源头元素。】

<input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="add"/>
export default {
  methods: {
    add(e){
      console.log(e.target.value);
     //e.target拿到的是事件触发的源头input元素节点
     //e.target.value:拿到input框里输入的数据
    }
  },
};
  • v-model:收集的就是输入框里的value值【一般表单里用,双向绑定】

  • 生成全球唯一的ID:uuid

  • nanoid【是uuid的精简版】:安装指令----npm i nanoid

  • 问题一: 把数据添加到list中,由于obj数据是在list中,添加的数据在myHeader中,属于兄弟关系,所以要做调整:把list的数据交给APP,myHeader添加的数据交给APP即可

  • 补充:data,props,methods,computed里面的数据都可以在vc或者vm上访问到。

  • trim() 方法:用于删除字符串的头尾空白符,空白符包括:空格、制表符 tab、换行符等其他空白符等

  • 给勾选框添加一个@change事件,只要勾选状态发生改变,就会调用函数【带参数】handleCheck(todo.id),并给该回调函数传了一个参数todo.id,即勾选状态发生改变的勾选框的ID

<input type="checkbox"  @change="handleCheck(todo.id)"/>
  • 注意:props中:父给子组件传数据【非函数】;子组件给父组件传递数据【必须是函数、方法】

  • 注意:props是只读的,vue底层会检测你对props的修改,如果进行了修改,控制台会报错,必须修改,就复制一份到data中,通过data去修改数据。

  • confirm()函数用于提供确认功能,它首先显示给定的message参数所包含的信息,并提供两个可选择的回答“ok”和“cancel”,然后等待用户选择其中的一个。如果用户选择“ok”则返回true;否则,如若选择“cancel”则返回false。

  • @ 是 v-on: 的简写形式

  • filter()方法的使用:返回一个包含了符合条件的所有元素新的数组,如果没有符合条件的元素则返回空数组。【元素组不会改变】

  • computed与methods实现相比,内部有缓存机制(复用),效率更高,调试方便

  • computed计算属性【比如:A】:第一次读取A属性时,就调用了getter,第2,3,4次读取A,就不会调用getter了

  • js中和数组有关的一个计数方法:reduce()

    reduce(A,B) 方法会遍历数组的每一项,他接收两个参数:

    • 第一个参数A:每次遍历都会调用的函数,而这个函数有接收四个参数,分别是:前一个值、当前项、项目的索引和数组对象,而这个函数的返回值,回传给下一次遍历时,执行的这个方法的第一个参数。
    • 第二个参数B:归并基础的初始值
data:{
    todos:[
          {id:'001',title:'练功夫',done:true},
		  {id:'002',title:'睡觉',done:false},
		  {id:'003',title:'打豆豆',done:true}
        ],
},
computed:{
doneTotal(){
      //此处使用reduce方法做条件统计【看有几个打钩了】
      const x = this.todos.reduce(
        // todo:遍历数组todos中的每一个元素
        // 比如:数组遍历时,把数组todos中的每一个元素分别赋值给todo【包含id='001'】
        (pre,todo)=>{
        //第一次: pre==0  todo-->当前值this.todos[0]
          console.log('@',pre,todo)
          return pre + (todo.done ? 1 : 0)
        }
      ,0)
      //这个0:统计的初始值==》reduce(第一个参数,第二个参数:0)
      return x
    },
}
//简写
doneTotal(){
 return this.todos.reduce((pre,todo)=>pre + (todo.done ? 1 : 0),0)
}
  1. 组件化编码流程:

    ​ (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​ (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​ 1).一个组件在用:放在组件自身即可。

    ​ 2). 一些组件在用:放在他们共同的父组件上(状态提升)。

    ​ (3).实现交互:从绑定事件开始。

  2. props适用于:

    ​ (1).父组件 ==> 子组件 通信

    ​ (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的

  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

App.vue文件

<template>
	<div id="root">
		<div class="todo-container">
			<div class="todo-wrap">
        <!-- 给每个组件添加多个不同的交互方法 -->
        <!-- 把App组件里的方法addTodo()传给TodoHeader组件【也需要用props去接受】 -->
				<TodoHeader :addTodo="addTodo"/>
				<TodoList 
         :todos="todos"
         :checkTodo="checkTodo"
         :deleteTodo="deleteTodo"
        />
				<TodoFooter
        :todos="todos"
        :checkAllTodo="checkAllTodo"
        :clearAllTodo="clearAllTodo"
        />
			</div>
		</div>
	</div>
</template>

<script>
//引入App的子组件
import TodoHeader from './components/TodoHeader'
import TodoFooter from './components/TodoFooter'
import TodoList from './components/TodoList'

  export default {
    name:'App',
    components: { TodoHeader,TodoFooter,TodoList },
    data() {
      return {
        //由于todos是TodoHeader组件和TodoFooter组件都在使用,所以放在App中(状态提升)
        todos:[
          {id:'001',title:'练功夫',done:true},
					{id:'002',title:'睡觉',done:false},
					{id:'003',title:'打豆豆',done:true}
        ],
      }
    },
    methods: {
      //添加一个todo
      addTodo(todoObj){
        // 在数组的开头添加一个数据
        this.todos.unshift(todoObj)
      },
      //勾选or取消勾选一个todo
      checkTodo(id){
        this.todos.forEach((todo) => {
          if(todo.id === id) todo.done = !todo.done
        })
      },
      // 删除一个todo
      // deleteTodo里面的id指的是:点击事件对应的id
      deleteTodo(id){
        // todo:this.todos这个数组的每一个元素
        this.todos=this.todos.filter(
          (todo)=>{
            return todo.id != id
          }
        )
      },
      //全选or取消全选
      checkAllTodo(done){
        this.todos.forEach(todo => todo.done = done)
      },
      // 清除所有已经完成的todo
      clearAllTodo(){
        this.todos= this.todos.filter(todo =>{
          return todo.done == false
          // 或者换成 return !todo.done
        }
        )
      },
    },

  }
</script>

<!-- style没有scoped属性:【全局样式】 -->
<!-- style有scoped属性:样式设置只在本组件里起作用【局部样式】 -->
<style>
	/*base*/
	body {
		background: #fff;
	}
  .btn {
    /* 行内的块级元素 */
		display: inline-block;
		padding: 4px 12px;
		margin-bottom: 0;
		font-size: 14px;
		line-height: 20px;
    /* 文本内容居中 */
		text-align: center;
    /* 垂直居中 */
		vertical-align: middle;
		cursor: pointer;
		box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
		border-radius: 4px;
	}
	.btn-danger {
    /* 字体颜色设置:白色 */
		color: #fff;
		background-color: #da4f49;
		border: 1px solid #bd362f;
	}
  /* 鼠标移动到删除按钮时 */
	.btn-danger:hover {
		color: #fff;
		background-color: #bd362f;
	}
	.btn:focus {
		outline: none;
	}

	.todo-container {
		width: 600px;
    /* 上下外边距为0,左右自动,实际效果为左右居中*/
		margin: 0 auto;
	}
  /* 后代选择器(包含选择器),选择到的是todo-container下面的所有后代todo-wrap  */
	.todo-container .todo-wrap {
		padding: 10px;
		border: 1px solid #67dbd1;
		border-radius: 5px;
	}
</style>

TodoHeader.vue文件

<template>
	<div class="todo-header">
    <!-- @keyup.enter="add" :按下回车按键,调用add方法 -->
		<input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="add" v-model="title"/>
	</div>
</template>

<script>
// 引入nanoid库生成ID号
import { nanoid } from 'nanoid'
export default {
	name: 'TodoHeader',
  //接收从App组件【父组件】传递过来的addTodo方法
  props:['addTodo'],
  data() {
    return {
      title: '',
    }
  },
  methods: {
    add(){
      // 如果输入框里为空,就跳过下面的代码,并弹窗
      if (!this.title.trim()) return alert('请输入值')
      //将用户的输入包装成一个todo对象
      const todoObj={id:nanoid(),title:this.title,done:false}
      //通知App组件去添加一个todo对象
      this.addTodo(todoObj)
      //清空输入
      this.title = ''
    }
  },
};
</script>

<style scoped>
    /* 头部样式设置 */
    /* 后代选择器(包含选择器),选择到的是todo-header下面的所有后代input */
    .todo-header input {
		width: 560px;
		height: 28px;
		font-size: 14px;
		border: 1px solid #ccc;
		border-radius: 4px;
    /* 内边距:上下4px,左右7px */
		padding: 4px 7px;
	}

  /* :focus获得焦点,并设置其新样式:例如:用户单击一个input输入框获取焦点,然后这个input输入框的边框样式就会发生改变,和其他的输入框区别开来,表明已被选中。 */
	.todo-header input:focus {
    /* outline 与 border 相似,不同之处在于 outline 在整个元素周围画了一条线;它不能像 border 那样,指定在元素的一个面上设置轮廓,也就是不能单独设置顶部轮廓、右侧轮廓、底部轮廓或左侧轮廓。 */
		outline: none;
    /* 定义边框的颜色 */
		border-color: rgba(82, 168, 236, 0.8);
    /* boxShadow 属性把一个或多个下拉阴影添加到框上 */
    /* 设置inset:内部阴影,不设置inset:外部阴影 */
    /* 【0 0】:不设置X轴与Y轴偏移量 */
    /* 第三个值【如10px,8px】:设置值阴影模糊半径为15px */
		box-shadow: inset 0 0 10px rgba(124, 56, 207, 0.075), 0 0 8px rgba(224, 58, 17, 0.6);
    background-color: bisque;
	}
</style>

TodoList.vue

<template>
	<ul class="todo-main">
		<TodoItem 
    v-for="todoObj in todos" 
    :key="todoObj.id" 
    :todo="todoObj"
    :checkTodo="checkTodo"
    :deleteTodo="deleteTodo"
    />
	</ul>
</template>

<script>
import TodoItem from './TodoItem'
export default {
	name: 'TodoList',
  components:{TodoItem},
  //声明接收App传递过来的数据,其中todos是自己用的,checkTodo和deleteTodo是给子组件TodoItem用的
  props: ['todos','checkTodo','deleteTodo']
};
</script>

<style scoped>
	/*main*/
	.todo-main {
    /* 左外边距:0px 【盒子贴着盒子】*/
		margin-left: 0px;
		border: 1px solid #ddd;
		border-radius: 2px;
		padding: 0px;
	}

	.todo-empty {
		height: 40px;
		line-height: 40px;
		border: 1px solid #ddd;
		border-radius: 2px;
		padding-left: 5px;
		margin-top: 10px;
	}
</style>

TodoItem.vue

<template>
	<li>
		<label>
			<input type="checkbox" 
      :checked="todo.done"
      @change="handleCheck(todo.id)"
      />
			<!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props -->
			<!-- <input type="checkbox" v-model="todo.done"/> -->
			<span>{{todo.title}}</span>
		</label>
		<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
	</li>
</template>

<script>
export default {
	name: 'TodoItem',
  // 声明接受从别的组件中的todoObj对象,todo、checkTodo、deleteTodo
  props: ['todo','checkTodo','deleteTodo'],
  methods: {
    //勾选or取消勾选【别弄混了:这里的id其实就是上面change事件中的todo.id】
    handleCheck(id){
      //change事件触发后,通知App组件将对应的todo对象的done值取反
      this.checkTodo(id)
    },
    //删除
    handleDelete(id){
      if (confirm('Are you sure you want to delete?')) {
        //点击事件触发后通知App组件将对应的todo对象删除
        this.deleteTodo(id)
      }
    }
  },
};
</script>

<style scoped>
 /*item*/
 li {
    /* ul无序列表 ol有序列表*/
    /* 列表前面无标记 */
		list-style: none;
    /* height定义了一个li盒子的高度 */
		height: 36px;
    /* 行高:指的是文字占有的实际高度 */
		line-height: 36px;
    /* 当height和line-height相等时,即盒子的高度和行高一样,内容上下居中 */
		padding: 0 5px;
    /* 边框底部:1px的实心线 颜色*/
		border-bottom: 1px solid #c0abc3;
	}

  /* 后代选择器(包含选择器),选择到的是li下面的所有后代label */
	li label {
    /* 左对齐浮动【元素一旦浮动就会脱离文档流(不占位,漂浮)】 */
	 /*	float: left; */
    /* 鼠标放在label元素上时变成小小手 */
		cursor: pointer;
	}

	li label input {
     /* 垂直居中 */
		vertical-align: middle;
		margin-right: 6px;
		position: relative;
		top: -1px;
	}

   /* 后代选择器(包含选择器),选择到的是li下面的所有后代button */
	li button {
    /* 向右浮动 */
		float: right;
    /* 不为被隐藏的对象保留其物理空间,即该对象在页面上彻底消失,通俗来说就是看不见也摸不到。 */
		display: none;
    /* 上边距为3px */
		margin-top: 3px;
	}

	li:before {
    /* initial:它将属性设置为其默认值。 */
		content: initial;
	}

   /* 结构伪类选择器 选择最后一个li元素 */ 
	li:last-child {
    /* 边框底部没有线 */
		border-bottom: none;
	}

	li:hover{
		background-color: #ddd;
	}
	
  /* 鼠标移动到该元素上时,将button按钮显示出来 */
	li:hover button{
    /*  display:block将元素显示为块级元素 */
		display: block;
	}
</style>

TodoFooter.vue

<template>
	<div class="todo-footer" v-show="total">
		<label>
			<!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> 
      可以用下面这行代码代替-->
      <!--对于type="checkbox",v-model绑定的就是一个布尔值,isAll为false or true  -->
			<input type="checkbox" v-model="isAll"/>
		</label>
		<span>
			<span>已完成{{ doneTotal }}</span> / 全部{{ total }}
		</span>
		<button class="btn btn-danger" @click="clearAllDone">清除已完成任务</button>
	</div>
</template>

<script>
export default {
	name: 'TodoFooter',
  props: ['todos','checkAllTodo','clearAllTodo'],
  computed:{
    //总数
    total(){
      return this.todos.length
    },
    // 已完成数
    doneTotal(){
      //此处使用reduce方法做条件统计
      /* return this.todos.reduce(
        // todo:遍历数组todos中的每一个元素
        // 比如:数组遍历时,把数组todos中的每一个元素分别赋值给todo【包含id='001'】
        (pre,todo)=>{
          // console.log('@',pre,todo)
          return pre + (todo.done ? 1 : 0)
        }
      ,0) */
      //简写
      return this.todos.reduce((pre,todo)=>pre + (todo.done ? 1 : 0),0)
    },
    //控制全选框
 /*    isAll(){
       //计算属性简写:isAll属性,只能被读取,不能被修改
      return this.total === this.doneTotal && this.total>0
    } */
    isAll:{
      //get有什么作用?当有人读取isAll时,get就会被调用,且返回值就作为isAll的值
			//get什么时候调用?1.初次读取isAll时。2.所依赖的数据发生变化时。
      get(){
        //全选框是否勾选  【&&:且】
        return this.total === this.doneTotal && this.total>0
      },
      //set什么时候调用? 当isAll被修改时。
      // value就是:v-model绑定的值false【未勾选】 or true【勾选】
      set(value){
        console.log(value)
        this.checkAllTodo(value)
      }
    },
  },
  methods: {
/*     checkAll(e){
      console.log(e.target.checked);
      // 拿到的是全选或者全不选的布尔值
      this.checkAllTodo(e.target.checked)
    } */

    // 清空所有已完成
    clearAllDone(){
      this.clearAllTodo()
    }
  },
};
</script>

<style scoped>
	/*footer*/
	.todo-footer {
		height: 40px;
		line-height: 40px;
		padding-left: 6px;
		margin-top: 5px;
	}

	.todo-footer label {
		display: inline-block;
		margin-right: 20px;
		cursor: pointer;
	}

	.todo-footer label input {
		position: relative;
		top: -1px;
		vertical-align: middle;
		margin-right: 5px;
	}

	.todo-footer button {
		float: right;
		margin-top: 5px;
	}
</style>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值