Vue2-TodoList案例(初级 后面会进行完善)

🥔:觉得累是因为在走上坡路

本案例是初级案例,在下面几节会进行完善——Vue.js

TodoList案例

    • 组件化编码流程(通用)
    • 整体思路
      • 1、分析结构
      • 2、拆html和css
      • 3、初始化列表
      • 4、实现添加列表功能
      • 5、实现勾选功能
      • 6、实现删除功能
      • 7、实现底部统计功能
      • 8、实现全选框的交互
        • (1)每个todo控制全选框
        • (2)全选框控制todo全选或者取消全选:使用点击事件实现。
        • (3)全选框控制todo全选或者取消全选:使用v-model实现。
      • 9、点击右下角按钮删除全部已完成任务
    • 案例完整代码展示
      • 1、App.vue
      • 2、TodolistHeader.vue
      • 3、TodolistList.vue
      • 4、TodolistItem.vue
      • 5、TodolistFooter.vue
    • 总结

组件化编码流程(通用)

  1. 实现静态组件:抽取组件,使用组件实现静态页面效果

  2. 展示动态数据:

    ①数据的类型、名称是什么?

    ②数据保存在哪个组件?

  3. 交互——从绑定事件监听开始

整体思路

TodoList类似于我们手机里的待办事项或者备忘录

请添加图片描述

1、分析结构

确定组件名称TodolistHeader,TodolistList,TodolistItem,TodolistFooter(下文会简写成Header、List、Item、Footer)和个数,还有嵌套关系(TodolistList里面包含TodolistItem,TodolistList和TodolistHello\TodolistFooter为兄弟组件),然后引入相应的组件。

  • 一个命名问题:此处命名不直接用Header、Footer等简单的命名是因为这些命名本身在标签具有特殊的含义(类似于关键字),并且Vue推荐我们使用多单词命名。为了达到和Vue开发者工具里的名字一样,这里使用的“大驼峰”命名(在驼峰命名的基础上首字母大写)。

请添加图片描述

请添加图片描述

2、拆html和css

一般的项目是写好html和css的,我们要把他们拆到相应的组件里,可以先全部放到App.vue中,然后打开开发者工具,观察他们的结构,分别放到对应的组件里面,style标签中加上scoped属性(保证当前样式只在当前组件中使用,防止样式名字不能重复使用)

3、初始化列表

在List中定义一个数组todos存储每个item为对象,然后在List的标签中写<Item></Item>并用v-for遍历数组中的每个对象生成结构,且通过自定义属性todo把每个对象传给子组件Item

在Item中使用props配置项接收(实现父子组件通信),然后input框里的checked绑定todo.done(可以初始化页面)

props配置项不熟悉的可以点击此处复习props

4、实现添加列表功能

上一步我们是在List中定义了todos数组,而用户的输入是在Header中,也就是说我们要在Header中收集数据,然后传给List,但是兄弟组件如何通信?全局事件总线、消息订阅与发布、Vuex都可以,但是现在还没学。

之前我们学的props配置项,可以实现父组件给子组件传数据(父组件里写子组件标签并配置属性,子组件使用props接收),现在我们想实现兄弟组件HeaderList的通信,可以借助App这个共同的父亲。

(1)把List中的数据todos定义在App里,然后传给List一份(通过props接收,由于是直接传到vc上,模板不会报错)。
(2)在App组件中定义一个函数,函数里面写个参数

	addTodo(x) {
            //借助这个函数,拿到Header中用户输入的东西
            this.todos.unshift(x);
       }

(3)把这个函数传给HeaderHeaderprops接收一下,然后在Header中调用这个函数,把用户的输入传给这个函数

const todoObj = { id: nanoid(), title: this.title, done: false };
this.addTodo(todoObj);

(4)由于addTodo是定义在App上的,所以App就直接拿到了函数的参数,然后就可以直接添加在todos中,这样的话todos改变,模板重新解析,传给Listtodos也改变,List模板重新解析,v-for一遍历,页面就多了一个。

5、实现勾选功能

选中:done=true,不选中:done=false。思路:简单来说就是让App拿到要修改的数据的id,找到这个数据然后把done属性取反。

我们先在App组件里定义一个函数,用来接收当前操作对象的id,然后函数里的逻辑是找到这个id,然后done属性取反(记住,数据源在哪里,修改数据的方法就写在哪里

changeTodo(id) {
            this.todos.forEach((todo) => {
                if (todo.id === id) todo.done = !todo.done;
            })
        }

然后把changeTodo这个函数通过标签和props传给List,再传给Item
然后在Item组件里定义一个函数handleChange,用来获取当前操作的多选框的id

handleChange(id) {
            // 不能像下面这样直接修改props的数据,数据源在哪里,我们就去哪里改
            // 如下代码也能实现功能,但是不建议这么写,理由就是不能直接改props传来的东西
            // this.todo.done = !this.todo.done;   
            //不报错是因为,改的不是整个对象(对象的地址),地址没变就不会报错

            //通知App要修改的数据的id是哪个
            this.changeTodo(id);
        }

handleChange函数通过点击事件来触发,实参就是当前input节点的todo的id

<input type="checkbox" :checked="todo.done" @click="handleChange(todo.id)" />

当然,这个勾选功能不用这么绕来绕去也可以实现,比如我直接在handleChange

this.todo.done = !this.todo.done;   

又或者我用v-model,一行代码就搞定了,不用在App再定义函数,然后传来传去

<input type="checkbox" v-model="todo.done" />

但是不建议这么写,理由就是不能直接改props传来的东西,记住,数据源在哪里,修改数据的方法就配置在哪里,千万记住props是只读的,如果改它,就会改源数据,这样所有用到数据的组件都会受到影响。

6、实现删除功能

和上面的类似,也是需要传递函数,不能动props。
首先App定义一个删除函数

deleteTodo(id) {
            this.todos = this.todos.filter((todo) => {
                return todo.id !== id;
            })
            // this.todos = this.todos.filter(todo => todo.id !== id);
        }

把这个函数传给List,再传给Item
Item中的按钮来个点击事件

<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>

把当前input的这个id传给App,调用App中的deleteTodo函数让App去操作

handleDelete(id) {
    if (confirm('确定要删除吗?'))  //点确定是true,取消是false
        this.deleteTodo(id);
}

7、实现底部统计功能

(1)App中的todos数据传给Footer一份
(2)全部比较简单,直接插值语法读数组todos的长度就行。已完成这里可以用计算属性

<span>已完成{{ doneTotal }}</span> / 全部{{ todos.length }}

(3)计算属性这里有多种实现方式:

写法1:forEach遍历

let count = 0;
this.todos.forEach(todo => {
    if (todo.done) count++;
});
return count;

写法2:for of遍历

let count = 0;
//for in 拿到的是数组的索引,for of才能拿到元素
for (let todo of this.todos) {
    if (todo.done === true)
        count++;
}
return count;

写法3:filter过滤

let count = this.todos.filter(todo => todo.done)
return count.length;

写法4:ES6的reduce

  • 对reduce不熟悉的可以点击此处复习reduce(点击目录里的数组核心方法即可)
const count = this.todos.reduce((pre, todo) => {
    return pre + (todo.done ? 1 : 0)
}, 0);
return count;
//return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);

8、实现全选框的交互

(1)每个todo控制全选框

当已完成和全部相等且不等于0时,勾选全选框,写在计算属性中

<input type="checkbox" :checked="isAll" " />
isAll(){
  return this.total === this.doneTotal && this.total !== 0;
  }

如果全部是0个,就隐藏Footer(用v-show实现)

(2)全选框控制todo全选或者取消全选:使用点击事件实现。

App中定义一个函数,用来接收Footer中全选框的状态值,然后把每个数据的done值都改成和全选框的状态值一样(选择or不选择)

checkAllTodos(isDone) {
            this.todos.forEach(todo => todo.done = isDone);
        },

Footer的全选框中,加入点击事件并定义一个函数

<input type="checkbox" :checked="isAll" @click="handleCheckAll" />

在函数中调用App中的函数,把状态传过去

handleCheckAll(e) {
            // this.checkAllTodos(!this.isAll);  
            this.checkAllTodos(e.target.checked);  //直接拿dom的值比较合适
        },

(3)全选框控制todo全选或者取消全选:使用v-model实现。

第一步一样,在App中定义一个函数,用来接收Footer中全选框的状态值,然后把每个数据的done值都改成和全选框的状态值一样(选择or不选择)

checkAllTodos(isDone) {
            this.todos.forEach(todo => todo.done = isDone);
        },

在Footer的全选框中,加入v-model(没有value绑定的是checked)绑定isAll计算属性

<input type="checkbox" v-model="isAll" />

isAll中加入setter,把每个更新的值传给App中的函数。这样就不用再另外定义methods了,只用计算属性就能搞定:item影响全选框(getter读isAll)全选框影响item(setter改isAll)两种情况

isAll: {
            get() {
                return this.total === this.doneTotal && this.total !== 0;
            },
            set(val) {
                this.checkAllTodos(val);
            }
        },

9、点击右下角按钮删除全部已完成任务

App中定义一个方法并传给Footer

//5.删除已完成任务
clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done
      })
    },

Footer中调用这个方法

<button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
clearAll() {
      this.clearAllTodo()//点击调用App里边的方法,把所有true删掉
    },

案例完整代码展示

1、App.vue

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <TodolistHeader :addTodo="addTodo" />
        <TodolistList
          :todos="todos"
          :changeTodo="changeTodo"
          :deleteTodo="deleteTodo"
        />
        <TodolistFooter
          :todos="todos"
          :checkAllTodo="checkAllTodo"
          :clearAllTodo="clearAllTodo"
        />
      </div>
    </div>
  </div>
</template>

<script>
import TodolistHeader from './components/TodolistHeader.vue'
import TodolistFooter from './components/TodolistFooter.vue'
import TodolistList from './components/TodolistList.vue'
export default {
  name: 'App',
  components: { TodolistHeader, TodolistFooter, TodolistList },
  data() {
    return {
      todos: [
        //通过App把todos传给List
        //id不用数值是因为数值是有尽头的,而字符串没有尽头
        { id: '001', title: '吃饭', done: true },
        { id: '002', title: '睡觉', done: true },
        { id: '003', title: '打豆豆', done: true },
      ],
    }
  },
  //记住,数据源在哪里,修改数据的方法就配置在哪里
  //最好不要在任何子组件中修改父组件的数据
  methods: {
    //1.添加一个todo
    addTodo(todoObj) {
      //借助这个函数,拿到Header中用户输入的东西
      this.todos.unshift(todoObj)
    },
    //2.勾选or取消勾选一个todo
    changeTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) todo.done = !todo.done
      })
    },
    //3.删除一个todo
    deleteTodo(id) {
      this.todos = this.todos.filter((todo) => {
        return todo.id !== id
      })
    },
    //4.全选or取消全选,其他子框同步
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done
      })
    },
    //5.删除已完成任务
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done
      })
    },
  },
}
</script>

<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;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

2、TodolistHeader.vue

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

<script>
import { nanoid } from 'nanoid'
export default {
  name: 'TodolistHeader',
  data() {
    return {
      title: '',
    }
  },
  props: ['addTodo'],
  methods: {
    // 输入内容按回车添加一个事件
    add() {
      //1.校验数据
      if (!this.title.trim()) return alert('输入不能为空')
      //2.将用户的输入包装成一个todo对象
      const todoObj = { id: nanoid(), title: this.title, done: false }
      //3.借助App里的函数,把用户的输入从Header传给App里面的todos 然后再通过App把todos传给List,用这种捷径(借助App)就巧妙地实现了Header和List的兄弟组件通信
      this.addTodo(todoObj)
      this.title = '' //4.输入完回车输入框变为空
    },
  },
}
</script>

<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}

.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
    0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

3、TodolistList.vue

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

<script>
import TodolistItem from './TodolistItem.vue'
export default {
  name: 'TodolistList',
  components: { TodolistItem },
  props: ['todos', 'changeTodo', 'deleteTodo'],
}
</script>

<style scoped>
/*main*/
.todo-main {
  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>

4、TodolistItem.vue

<template>
  <li>
    <label>
      <input
        type="checkbox"
        :checked="todo.done"
        @change="handleChange(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: 'TodolistItem',
  //声明接收todo对象
  props: ['todo', 'changeTodo', 'deleteTodo'],
  methods: {
    handleChange(id) {
      // 不能像下面这样直接修改props的数据,数据源在哪里,我们就去哪里改
      // 如下代码也能实现功能,但是不建议这么写,理由就是不能直接改props传来的东西
      // this.todo.done = !this.todo.done;
      //不报错是因为,改的不是整个对象(对象的地址),地址没变就不会报错

      //通知App要修改的数据的id是哪个
      this.changeTodo(id)
    },
    handleDelete(id) {
      //confirm点确定是true,取消是false
      if (confirm('确定删除这个事项吗?')) this.deleteTodo(id)
    },
  },
}
</script>

<style scoped>
/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}

li label {
  float: left;
  cursor: pointer;
}

li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}

li button {
  float: right;
  display: none;
  margin-top: 3px;
}

li:before {
  content: initial;
}

li:last-child {
  border-bottom: none;
}

li:hover {
  background-color: #ddd;
}

li:hover button {
  display: block;
}
</style>

5、TodolistFooter.vue

<template>
  <!-- 这里total如果显示是0(false)就隐藏(v-show)-->
  <div class="todo-footer" v-show="total">
    <label>
      <input type="checkbox" :checked="isAll" @change="checkAll" />
    </label>
    <span>
      <span>已完成{{ doneTotal }}</span> / 全部{{ total }}
    </span>
    <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
  </div>
</template>

<script>
export default {
  name: 'TodolistFooter',
  props: ['todos', 'checkAllTodo', 'clearAllTodo'],
  computed: {
    total() {
      return this.todos.length
    },
    doneTotal() {
      return this.todos.reduce(
        (pre, current) => pre + (current.done ? 1 : 0),
        0
      )
    },
    isAll() {
      return this.doneTotal === this.total && this.total > 0
    },
  },
  methods: {
    checkAll(e) {
      this.checkAllTodo(e.target.checked)
    },
    clearAll() {
      this.clearAllTodo() //点击调用App里边的方法,把所有为true的删掉
    },
  },
}
</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>

总结

1.组件化编码流程:
(1)拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2)实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

  • 一个组件在用:放在组件自身即可。
  • 一些组件在用:放在他们共同的父组件上(状态提升)。

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

2.props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/82832.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第三讲:ApplicationContext的实现

这里写目录标题 一、前文回顾二、基础代码准备三、基于XML的ClassPathXmlApplicationContext1. 创建spring-config.xml配置文件2. 指定配置文件的路径 四、基于注解的AnnotationConfigApplicationContext1. 新增一个配置类2.指定配置类信息 五、基于注解和ServletWebServer应用…

Endnote在线链接pubmed的时候报错12057:不能连接到吊销服务器,或者未能获得最终响应?

​嘎嘎嘎问题如下&#xff1a; 解决办法&#xff1a; 打开控制面板: ok,完了之后再去EndNote就不会出现此问题了。&#xff08;有的可能需要重启电脑&#xff0c;重启EndNote才会生效&#xff09;

Docker 网络之 ipvlan 和 macvlan

Docker ipvlan 和 macvlan 引言 本文讲解了Docker 网络模式中的 ipvlan 和 macvlan 的区别,目前自己在生产环境中使用的 ipvlan 模式非常问题.也解决了实际业务问题. IPvlan L2 mode example ipvlan 无需网卡混杂模式 , 运行如下命令后可以生成一个 vlan 子接口 , 会和主网卡…

Ajax介绍

1.与服务器进行数据交换&#xff1a;通过 Ajax 可以给服务器发送请求&#xff0c;并获取服务器响应的数据。 2.异步交互&#xff1a;可以在 不重新加载整个页面 的情况下&#xff0c;与服务器交换数据并 更新部分网页 的技术&#xff0c;如&#xff1a; 搜索联想、用户名是否可…

SpringMVC之异常处理

SpringMVC之异常处理 异常分为编译时异常和运行时异常&#xff0c;编译时异常我们trycatch捕获&#xff0c;捕获后自行处理&#xff0c;而运行时异常是不可预期的&#xff0c;就需要规范编码来避免&#xff0c;在SpringMVC中&#xff0c;不管是编译异常还是运行时异常&#xff…

【教程】华南理工大学校园网登录抓包和协议模拟

每次手动登录特别麻烦&#xff0c;而且时不时断一下&#xff0c;因此搞个脚本让它定时监测、断开重连比较方便。这里不讲这个脚本怎么写&#xff0c;只记录一下登录时的抓包内容。 蒜了&#xff0c;直接上解析吧&#xff0c;也不复杂&#xff0c;相信大家一目了然。 目录 抓包…

JavaScript中的变量声明方式有哪些?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 变量声明方式var 声明&#xff08;ES5及以前&#xff09;let 声明&#xff08;ES6以后&#xff09;const 声明&#xff08;ES6以后&#xff09; ⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者…

Docker自学:利用FastAPI建立一个简单的web app

环境配置&#xff1a;下载Docker Desktop 文件一&#xff1a;main.py from typing import Unionfrom fastapi import FastAPIimport uvicornapp FastAPI()app.get("/") def read_root():return {"Hello": "World"}app.get("/items/{item…

vs2022配置opencv进行监控 c++

下载opencv文件 下载好的目录结构是 以上就是用到的文件和目录 在vs2022配置 最后&#xff1a;此处运行提示找不到 opencv_world480.dll 解决办法&#xff1a;直接从 复制到windows下

【ElasticSearch】一键安装ElasticSearch与Kibana以及解决遇到的问题

目录 一、安装ES 二、安装Kibana 三、遇到的问题 一、安装ES 按顺序复制即可 docker network create es-net # 创建网络 docker pull images:7.12.1 # 拉取镜像 mkdir -p /root/es/data # 创建数据卷 mkdir -p /root/es/plugins # 创建数据卷 chmod 777 /root/es/** # 设置权…

基于Java+SpringBoot+Vue的学校田径运动会管理系统【源码+论文+演示视频+包运行成功】

博主介绍&#xff1a;✌擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案…

RT-Thread 的环形缓冲区 ---- 镜像指示位

可以看一下这篇我写的博客&#xff0c;了解一下大概&#xff1a; RingBuffer 环形缓冲区----镜像指示位_呵呵哒(&#xffe3;▽&#xffe3;)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/132340883?spm1001.2014.3001.5501 【回顾】缓冲区…

Linux:shell脚本:基础使用(5)《正则表达式-sed工具》

sed是一种流编辑器&#xff0c;它是文本处理中非常中的工具&#xff0c;能够完美的配合正则表达式使用&#xff0c;功能不同凡响。 处理时&#xff0c;把当前处理的行存储在临时缓冲区中&#xff0c;称为“模式空间”&#xff08;pattern space&#xff09;&#xff0c;接着用s…

微服务概述-7

Shiro 框架 Shiro 是一个用于 Java 应用程序的安全框架。它提供了身份验证、授权、加密和会话管理等功能&#xff0c;可以帮助开发人员构建安全可靠的应用程序。 Java 中针对权限管理常见的有 2 个著名的框架&#xff1a;spring security 和 shiro shiro 基本概念 credentia…

AI引擎助力,CamScanner智能高清滤镜开启扫描新纪元!

文章目录 ⭐ 写在前面⭐ 突破图像处理难点&#xff1a;扫描全能王的独特优势⭐ 耳听为虚&#xff0c;眼见为实⭐ 产品背后的主要核心&#xff1a;AI-Scan助力⭐ 深度学习助力智能文档处理的国际化进程⭐ 品味智能文档处理的轻松与精准 ⭐ 写在前面 在数字化快速发展的今天&…

AE-卡通人物解说动画视频的制作

目录 1.导入卡通人物图片和音频文件 2.新建合成 3.在卡通人物图片上添加效果和表达式 4.在音频文件上添加效果和表达式 5.将卡通人物中的 CC Split2 中分割1 表达式链接到滑块中 6.卡通人物根据音频文件自动匹配口型。 AE制作卡通人物解说视频&#xff0c;卡通人物口型根据…

服务监控平台:SpringBoot Admin入门应用

前言 在日常工作中&#xff0c;我们需要有一款监控平台来帮助我们管理服务&#xff0c;监控服务是否宕机、服务运行指标&#xff08;内存、虚拟机、线程、请求等&#xff09;、监控日志、管理服务&#xff08;服务下线&#xff09;等&#xff0c;SpringBoot Admin作为一款开源…

分布式 | 如何搭建 DBLE 的 JVM 指标监控系统

本篇文章采用 Docker 方式搭建 Grafana Prometheus 实现对 DBLE 的 JVM 相关指标的监控系统。 作者&#xff1a;文韵涵 爱可生 DBLE 团队开发成员&#xff0c;主要负责 DBLE 需求开发&#xff0c;故障排查和社区问题解答。 本文来源&#xff1a;原创投稿 爱可生开源社区出品&a…

湘潭大学 湘大 XTU OJ 1217 A+B VII 题解(非常详细)

链接 1217 题目 题目描述 小明非常高兴你能帮他处理那些罗马数字&#xff0c;他想学着自己写罗马数字&#xff0c;但是他不知道自己到底写对了没有。 请你帮他写个程序&#xff0c;能正确地将10进制数字转换成罗马数字&#xff0c;以便他能进行核对。 罗马数字是使用字母组…

《修图大杀器》PS beta 25.0最新版安装(无需魔法)和Draggan(拖拽式修图)安装

个人网站&#xff1a;https://tianfeng.space 文章目录 psbeta下载安装1.注册2.安装ps beta2.安装神经网络滤镜3.使用 Draggan下载安装 psbeta下载安装 链接&#xff1a;https://pan.baidu.com/s/1XbxSAFoXh0HDz6YbkrAzDg 提取码&#xff1a;e8pn 1.注册 https://account.a…