Vue3最佳实践 第七章 TypeScript 创建Trello 任务管理器

news2024/11/17 17:42:27

| 在这里插入图片描述

  ​ 我们将探讨如何使用Vue.js从零开始创建一个类似于Trello的任务管理应用程序。如果你不熟悉Trello,它是一款非常流行的任务管理工具,允许你把任务写在卡片上,然后通过一个看板的方式来直观地管理这些任务。Trello不仅可以用于个人的任务管理,还可以作为团队协作工具,在许多公司和组织中得到了广泛的应用。我们的目标是创建一个具有Trello核心功能的应用程序,包括创建任务卡片,以及通过拖放操作来移动这些卡片,以此来改变任务的状态或优先级。

  ​ 在这里我们将使用Vue.js来实现一个简单的Trello功能。这篇文章将分为两部分。在第一部分,我们将探讨如何使用Vue.js的Draggable属性和拖动事件来实现任务的移动和类别(列)的创建。我们将详细介绍如何设置这些功能,并提供代码示例来帮助你理解。在第二部分,我们将深入讨论如何使用TypeScript和Vue的Composition API来描述我们的应用程序。可以更好的了解如何由多个组件(例如 props 和 emit)组成的应用程序中设置 TypeScript。

  ​ 我们将使用前面介绍的TypeScript知识点来完成Trello功能的实现,系统这些代码将为你理解TypeScript提供有价值的信息和实践经验。我们将尽可能清晰地解释每一步,以便你能够跟上并理解我们正在做什么。阅读完这篇文章后,你将能够创建一个功能齐全的任务管理应用程序,并有足够的知识去定制和扩展它以满足你自己的需求。如果你是一个初学者,或者你想要了解如何使用拖动进行移动处理,那么这篇文章将会是一个很好的起点。

第一章 Vue3项目创建 1 Vue CLI 创建vue项目
第一章 Vue3项目创建 2 使用 Webpack 5 搭建 vue项目
第一章 Vue3项目创建 3 Vite 创建 vue项目
第二章 Vue3 基础语法指令
第三章 Vue Router路由器的使用
第四章 VUE常用 UI 库 1 ( element-plus,Ant ,naiveui,ArcoDesign)
第四章 VUE常用 UI 库 2 ( ailwind 后台框架)
第五章 Vue 组件应用 1( Props )
第五章 Vue 组件应用 2 ( Emit )
第五章 Vue 组件应用 3( Slots )
第五章 Vue 组件应用 4 ( provide 和 inject )
第五章 Vue 组件应用 5 (Vue 插件)
第六章 Pinia,Vuex与axios,VueUse 1(Pinia)
第六章 Pinia,Vuex与axios,VueUse 2(Vuex)
第六章 Pinia,Vuex与axios,VueUse 3(VueUse)
第六章 Pinia,Vuex与axios,VueUse 4(axios)
第七章 TypeScript 上
第七章 TypeScript 中
第七章 TypeScript 下 创建Trello 任务管理器

1 导入UI样式

npm init vite@latest zht-trello
cd zht-trello
npm install
npm run dev
npm install --save-dev @arco-design/web-vue

我们使用字节的UIarco-design来作为这个例子的主体架构。

2 创建任务数据

在src文件夹下创建文件夹data,在data中创建两个文件,创建class.json和tasks.json文件。

class.json

[
  {
    "id": 1,
    "name": "看板任务一",
    "collapsed": false
  },
  {
    "id": 2,
    "name": "看板任务二",
    "collapsed": false
  },
  {
    "id": 3,
    "name": "看板任务二",
    "collapsed": false
  }
]

tasks.json

[
    {
      "id": 1,
      "category_id": 1,
      "name": "任务1",
      "start_date": "2022-12-18",
      "end_date": "2022-12-20",
      "incharge_user": "张童",
      "percentage": 100
    },
    {
      "id": 2,
      "category_id": 1,
      "name": "任务1",
      "start_date": "2020-12-19",
      "end_date": "2020-12-23",
      "incharge_user": "王鑫",
      "percentage": 90
    },
    {
      "id": 3,
      "category_id": 3,
      "name": "任务3",
      "start_date": "2022-12-19",
      "end_date": "2022-12-21",
      "incharge_user": "王鑫",
      "percentage": 40
    },
    {
      "id": 4,
      "category_id": 2,
      "name": "任务4",
      "start_date": "2022-12-21",
      "end_date": "2022-12-30",
      "incharge_user": "李佳",
      "percentage": 60
    },
    {
      "id": 5,
      "category_id": 2,
      "name": "任务5",
      "start_date": "2022-12-20",
      "end_date": "2022-12-22",
      "incharge_user": "王芳",
      "percentage": 5
    },
    {
      "id": 6,
      "category_id": 1,
      "name": "任务6",
      "start_date": "2022-12-28",
      "end_date": "2022-12-08",
      "incharge_user": "王芳",
      "percentage": 0
    }
  ]

  ​ 使用 ref 函数将class.json与tasks.json保存成反应数据,方便在后边的代码中使用。

<script setup lang="ts">
import class_data from "./data/class.json";
import task_data from "./data/tasks.json";
import { ref } from "vue";
const classes = ref(class_data);
const tasks = ref(task_data);
</script>
<template>
</template>
<style scoped>
</style>

3 ref 函数的类型设置

  ​ 现在我们使用 TypeScript 为 ref 函数来设置类型。尽管代码会自动执行 TypeScript 的类型推断功能,即使没有显式设置类型,也不会显示错误消息,但是我们仍然建议尽可能地显式声明类型,以提高代码的可读性和可维护性。

  ​ 首先,我们需要创建一个用于管理应用程序中使用的类型的文件夹。在 src 文件夹中创建一个名为 types 的新文件夹。由于我们要创建的应用程序规模较小,因此我们将所有类型都存储在一个名为 index.ts 的文件中,并将它们导出以供我们的组件使用。

  ​ 应用程序中有两种主要的类型:类别 (CateClass) 和任务 (Task)。这些类型是基于固定 JSON 文件数据定义的。在类型定义中我们也可以使用 Type 而不是 Interface。

export interface CateClass {
    id: number;
    name: string;
    collapsed?: boolean;
  }
  
  export interface Task {
    id: number;
    category_id: number;
    name: string;
    start_date: string;
    end_date: string;
    incharge_user: string;
    percentage: number;
  }

  ​ 请注意,标有 ? 的属性是可选的,因此即使缺少该属性也不会发生错误。

  ​ 现在我们已经定义了类型,接下来我们需要在 App.vue 文件中导入这些类型,并使用 ref 函数来设置它们。这里是如何做到这一点的:

<script setup lang="ts">
import type {CateClass, Task } from "./data/tasksIndex";
import class_data from "./data/class.json";
import task_data from "./data/tasks.json";
import { ref } from "vue";
 // 使用 ref 函数和我们定义的类型来设置 classes 和 tasks 的类型
const classes = ref<CateClass[]>(class_data);
const tasks = ref<Task[]>(task_data);
</script>
<template>
</template>
<style scoped>
</style>

  ​ 在上面的代码中,ref 函数用于创建一个响应式的引用,其值可以被改变。我们使用 <CateClass[]><Task[]> 来显式地指定 classestasks 的类型,这样就可以确保它们总是包含正确类型的数据。

4 设置计算属性

  ​ 创建了一个名为 renderCategoryTask 的计算属性函数。该计算属性通过遍历 classes 数组,并根据每个类别的 id 属性筛选出对应的任务,生成一个新的数组。这样,我们就得到了一个包含类别和对应任务的嵌套数据结构。就可以在模板中使用这个计算属性来渲染类别和任务组件。

  ​ 在模板中我们使用 v-for 指令遍历 renderCategoryTask 数组,并将每个类别渲染为一个 a-col 组件。在 a-card 组件中,我们显示了类别的名称,并使用 v-if 条件指令来判断是否有任务,如果有则渲染任务列表。

<script setup lang="ts">
import type {CateClass, Task,CateClassTask } from "./data/tasksIndex";
import class_data from "./data/class.json";
import task_data from "./data/tasks.json";
import { ref, computed } from "vue";
const classes = ref<CateClass[]>(class_data);
const tasks = ref<Task[]>(task_data);
const renderCategoryTask = (() => {
  return classes.value.map((classes) => {
    const filterTasks = tasks.value.filter(
      (task) => task.category_id === classes.id
    );
    return {
      id: classes.id,
      name: classes.name,
      tasks: filterTasks,
    };
  });
});
</script>
<template>
<div
    :style="{
      boxSizing: 'border-box',
      width: '100%',
      padding: '40px',
      backgroundColor: 'var(--color-fill-2)',
    }"
  >
  <h1 >Trello 任务管理</h1>
    <a-row :gutter="20" :style="{ marginBottom: '20px' }">
      <a-col :span="8" v-for="category in renderCategoryTask" :key="category.id">
        <a-card :title="category.name" 
          :bordered="false" 
          :style="{ width: '100%',height:'300px'}">
          <template #extra>
            <a-link>详细</a-link>
          </template>
          <a-list  v-for="task in category.tasks" :key="task.id">
            <a-list-item>{{task.name}} ||  {{task.incharge_user}}</a-list-item>
          </a-list>
        </a-card>
      </a-col>
    </a-row>
</div>
</template>
<style scoped>
</style>      

5 组件设置

  ​ 现在我们要将类别和任务的描述组件化。通过将其做成一个组件,大家可以加深对使用 TypeScript 时,通过 emit 来设置 props 和事件设置的理解。

  ​ 在 CategoryItem.vue 文件中,我们使用 defineProps 来设置从 props 传递过来的 categoryTask。defineProps 不需要执行 import。defineProps 指定使用泛型传递的 props 类型。这里的 CategoryTask 是类型。

<template>
  <a-col :span="8">
    <a-card :title="category?.name" :bordered="false" :style="{ width: '300px' }">
      <template #extra>
        <a-link>详细</a-link>
      </template>
      <a-list v-for="task in category.tasks" :key="task.id">
        <TaskItem :task="task" draggable="true"></TaskItem>
      </a-list>
    </a-card>
  </a-col>
</template>

CategoryItem.vue

  ​ 首先,在组件文件夹中创建一个 CategoryItem.vue 文件。起初,只有 script 和 template 标签没有描述任何处理。

<script setup lang="ts">
import type { CateClassTask } from "../data/tasksIndex";
import TaskItem from "./TaskItem.vue";
interface Props {
    category: CateClassTask;
}
defineProps<Props>();
</script>
<template>
<a-col :span="8"  >
        <a-card :title="category?.name" 
          :bordered="false" 
          :style="{ width: '100%',height:'300px'}">
          <template #extra>
            <a-link>详细</a-link>
          </template>
          <a-list  v-for="task in category.tasks" :key="task.id" >
          <TaskItem :task="task"  draggable="true"/>
          </a-list>
        </a-card>
      </a-col>
</template>

​ 此外,你还可以创建 TaskItem 组件来显示任务。对于 TaskItem 组件,描述如下,这样你就可以在 props 中接收任务。

TaskItem.vue

<script setup lang="ts">
import type { Task } from "../data/tasksIndex";
defineProps<{
  task: Task;
}>();
</script>
<template>
    <a-list-item>{{task.name}} || {{task.incharge_user}}</a-list-item>
</template>

  ​ 在 CategoryItem.vue 文件中导入创建的 TaskItem 组件。在模板标签中使用导入的 TaskItem 组件。对于 props,在 TaskItem.vue 文件中的 props 任务集中传递使用 v-for 指令展开的任务。

6 可拖动设置

  ​ 我们将实现同一类别(列)内的任务移动,我们需要将任务元素设置为可拖动。首先在CategoryItem.vue代码中,添加 draggable 属性并将其设置为 true 以使元素可拖动。设置一个setDragTask拖动事件,通过这个单击事件来抓取拖动组件中的元素,并在按下鼠标按钮的同时移动元素。

<TaskItem :task="task" 
  draggable="true" //设置为可以移动
  @dragstart="setDragTask"
/>
<script setup lang="ts">
//设置移动事件
const setDragTask = () => {
  console.log('drag');
};
</script>

  ​ 在dragstart事件中设置setDragTask方法并检查操作。v-on指令可用于配置事件,这里使用缩写@。当在浏览器中移动组件的时候,在开发者工具控制台中会显示字符串“drag”。

7 移动被拖动元素

1 在CategoryItem.vue组件中设置dragover事件。在dragover事件中调用dragOverTask函数,并将task作为参数传递进去。

<CategoryItem
  class="min-w-[400px]"
  v-for="categoryTask in renderCategoryTask"
  :key="categoryTask.id"
  :categoryTask="categoryTask"
  @setDragTask="setDragTask"
  @dragOverTask="dragOverTask"
/>

2 CategoryItem.vue脚本模块里在emit中,将事件名称设置为dragOverTask,并像之前的dragstart事件一样使用emit将任务传递给父组件。

const emit = defineEmits<{
  (e: "setDragTask", task: Task): void;
  (e: "dragOverTask", task: Task): void;
}>();

const setDragTask = (task: Task) => {
  emit("setDragTask", task);
};

const dragOverTask = (task: Task) => {
  emit("dragOverTask", task);
};

3 在dragOverTask函数中,检查传递过来的overTask信息以及之前设置的dragTask内容,以进行操作

const dragOverTask = (overTask: Task) => {
  console.log('task:', dragTask.value);
  console.log('overTask:', overTask);
};

4 在检查操作时,只有当dragTask的id和overTask的id不同时才执行移动操作。注意,我们在dragTask.value后面添加了?,因为dragTask的值可能为null,如果不添加?,将会显示错误消息。

const dragOverTask = (overTask: Task) => {
  if (dragTask.value?.id !== overTask.id) {
    const deleteIndex = tasks.value.findIndex(
      (task) => task.id === dragTask.value?.id
    );
    const addIndex = tasks.value.findIndex((task) => task.id === overTask.id);
    if (dragTask.value !== null) {
      tasks.value.splice(deleteIndex, 1);
      tasks.value.splice(addIndex, 0, dragTask.value);
    }
  }
};

8 trello 代码

trello项目结构

zht-trello
   |---node_modules
   |---public
   |    |--index.html    // 项目启动页面
   |---src               // 代码源文件
   |    |--assets        // 资源目录
   |    |--components    // 组件目录
   |    |   |-- CategoryItem.vue   // 看板程序
   |    |   |-- TaskItem.vue       // 任务条
   |    |   |-- data
   |    |   |	|-- class.json    //
   |    |   |	|-- tasks.json   
   |    |   |	|-- tasksIndex.ts   
   |    |--main.js       // 入口文件
   |    |--App.vue       // 主程序
   |----package.json

main.ts

import { createApp } from 'vue'
import App from './App.vue'
import ArcoVue from '@arco-design/web-vue';
import '@arco-design/web-vue/dist/arco.css';
const app = createApp(App)
app.use(ArcoVue);
app.mount('#app')

App.vue

<script setup lang="ts">
import CategoryItem from "./components/CategoryItem.vue";
import type {CateClass, Task,CateClassTask } from "./data/tasksIndex";
import class_data from "./data/class.json";
import task_data from "./data/tasks.json";
import { ref, computed } from "vue";
const classes = ref<CateClass[]>(class_data);
const tasks = ref<Task[]>(task_data);
const dragTask = ref<Task | null>(null);
const renderCategoryTask = computed(() => {
  return classes.value.map((classes) => {
    const filterTasks:Task[] = tasks.value.filter(
      (task) => task.category_id === classes.id
    );
    return {
      id: classes.id,
      name: classes.name,
      tasks: filterTasks,
      collapsed:classes.collapsed
    } as CateClassTask;
  })
});
const setDragTask = (task: Task) => {
  dragTask.value = task;
};

const dragOverTask = (overTask: Task) => {
  if (dragTask.value?.id !== overTask.id) {
    const deleteIndex = tasks.value.findIndex(
      (task) => task.id === dragTask.value?.id
    );
    const addIndex = tasks.value.findIndex((task) => task.id === overTask.id);
    if (dragTask.value !== null) {
      tasks.value.splice(deleteIndex, 1);
      dragTask.value.category_id = overTask.category_id; //追加
      tasks.value.splice(addIndex, 0, dragTask.value);
    }
  }
};

const dragOverCategory = (categoryTask: CateClassTask) => {
  if (dragTask.value?.category_id !== categoryTask.id) {
    const filterTasks = tasks.value.filter(
      (task) => task.category_id === categoryTask.id
    );
    if (filterTasks.length === 0 && dragTask.value !== null)
      dragTask.value.category_id = categoryTask.id;
  }
};
</script>
<template>
<div
    :style="{
      boxSizing: 'border-box',
      width: '100%',
      padding: '40px',
      backgroundColor: 'var(--color-fill-2)',
    }"
  >
  <h1 >Trello 任务管理</h1>
    <a-row :gutter="20" :style="{ marginBottom: '20px' }">
      <CategoryItem
        v-for="category in renderCategoryTask"
        :key="category.id"
        :category="category"
        @dragover="dragOverCategory(category)"
        @setDragTask="setDragTask"
        @dragOverTask="dragOverTask"
      />
    </a-row>
</div>
</template>
<style scoped>
</style>

CategoryItem.vue

<script setup lang="ts">
import type { Task,CateClassTask } from "../data/tasksIndex";
import TaskItem from "./TaskItem.vue";
interface Props {
    category: CateClassTask;
}
defineProps<Props>();
const emit = defineEmits<{
  (e: "setDragTask", task: Task): void;
  (e: "dragOverTask", task: Task): void;
}>();

const setDragTask = (task: Task) => {
  emit("setDragTask", task);
};

const dragOverTask = (task: Task) => {
  emit("dragOverTask", task);
};
</script>
<template>
<a-col :span="8"  >
        <a-card :title="category?.name" 
          :bordered="false" 
          :style="{ width: '100%',height:'300px'}"
          >
          <template #extra>
            <a-link>详细</a-link>
          </template>
          <a-list  v-for="task in category.tasks" :key="task.id" >
          <TaskItem :task="task"  
            draggable="true"
            @dragstart="setDragTask(task)"
            @dragover="dragOverTask(task)"
           />
          </a-list>
        </a-card>
      </a-col>
</template>

TaskItem.vue

<script setup lang="ts">
import type { Task } from "../data/tasksIndex";
defineProps<{
  task: Task;
}>();
</script>
<template>
    <a-list-item>{{task.name}} ||  {{task.incharge_user}}</a-list-item>
<emplate>

tasksIndex.ts

export interface CateClass {
    id: number;
    name: string;
    collapsed?: boolean;
  }
  export interface Task {
    id: number;
    category_id: number;
    name: string;
    start_date: string;
    end_date: string;
    incharge_user: string;
    percentage: number;
  }
  export interface CateClassTask {
    id: number;
    name: string;
    collapsed?: boolean;
    tasks: Task[];
  }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

报名通道开启 | 第六届“强网”拟态防御国际精英挑战赛强势来袭

第六届“强网”拟态防御国际精英挑战赛计划将于2023年11月下旬在南京震撼开幕。 本届比赛采用线上线下结合的形式&#xff0c;再次为全球顶尖战队提供实战机会&#xff0c;向多类拟态防御设备系统发起挑战。接受挑战的拟态防御设备系统基于邬江兴院士原创的网络空间内生安全理…

向量空间的封闭性

向量空间封闭&#xff0c;是指&#xff1a; - 两个向量相加所得的向量仍然在该向量空间中 - 实数和向量数乘所得的向量仍然在该向量空间中 即&#xff0c;假设为向量的集合&#xff1a; 如果&#xff0c;&#xff0c;那么如果&#xff0c;&#xff0c;那么

电梯安全监测丨S271W无线水浸传感器用于电梯机房/电梯基坑水浸监测

城市化进程中&#xff0c;电梯与我们的生活息息相关。高层住宅、医院、商场、学校、车站等各种商业体建筑、公共建筑中电梯为我们生活工作提供了诸多便利。 保障电梯系统的安全至关重要&#xff01;特别是电梯机房和电梯基坑可通过智能化改造提高其安全性和稳定性。例如在暴风…

电力行业首个自主可控的大模型发布了!百度飞桨、文心大模型提供支持

电力行业首个自主可控的大模型来了&#xff01;9月26日&#xff0c;南方电网人工智能科技有限公司负责研发的电力行业人工智能创新平台及自主可控电力大模型正式公开发布。 南方电网举办电力行业人工智能创新平台及自主可控电力大模型发布会 电力行业人工智能创新平台提供模型…

总结一:C++面经(五万字长文)

文章目录 一、C基础部分1、C特点。2、说说C语言和C的区别。3、说说 C中 struct 和 class 的区别。4、 include头文件的顺序以及双引号""和尖括号<>的区别。5、说说C结构体和C结构体的区别。6、导入C函数的关键字是什么&#xff0c;C编译时和C有什么不同&#x…

期望最大化(EM)算法:从理论到实战全解析

目录 一、引言概率模型与隐变量极大似然估计&#xff08;MLE&#xff09;Jensen不等式 二、基础数学原理条件概率与联合概率似然函数Kullback-Leibler散度贝叶斯推断 三、EM算法的核心思想期望&#xff08;E&#xff09;步骤最大化&#xff08;M&#xff09;步骤Q函数与辅助函数…

城乡供水智慧化运营,喜提一等奖!

近日&#xff0c;第六届“绽放杯”5G应用征集大赛江西区域赛——5G智慧住建行业赛结果揭晓。由江西省水务集团、江西电信、天翼物联、熊猫智慧水务、江西普适科技联合申报的《5GPLC安全AIoT&#xff0c;助力江西水务城乡供水智慧化运营》项目获一等奖。 水务行业作为国民经济发…

vue的几个提效技巧

1.动态组件 <component :is组件名></component> 结合v-for循环使用 使用环境 如图&#xff0c;这是一个v-for渲染的列表(只是目前这个版块才刚开始做&#xff0c;目前只有一个)&#xff0c;圆圈内的就是一个组件&#xff0c;也就是要v-for动态组件 实际使用 一…

Linux基本指令(中)——“Linux”

各位CSDN的uu们好呀&#xff0c;今天&#xff0c;小雅兰的内容是Linux基本指令呀&#xff01;&#xff01;&#xff01;下面&#xff0c;让我们进入Linux的世界吧&#xff01;&#xff01;&#xff01; cp指令&#xff08;重要&#xff09; mv指令&#xff08;重要&#xff09…

外汇天眼:业务员离职,也不给出金!Sky Alliance Markets摆烂不玩了?

近段时间&#xff0c;外汇天眼收到Sky Alliance Markets的客诉激增已达10条&#xff0c;目前该平台的官网还能打开。但最近关于Sky Alliance Markets是否跑路的争议也越来越多&#xff0c;据来外汇天眼投诉的用户透露&#xff0c;Sky Alliance Markets的员工大部分已经离职&…

H3C交换机 DEV/1/FAN_DIRECTION_NOT_PREFERRED

1.现象 DEV/1/FAN_DIRECTION_NOT_PREFERRED: Fan 1 airflow direction is not preferred on slot 1, please check it. 2.解决方法&#xff1a; 查看下设备风扇的颜色&#xff0c;风扇分为红色与蓝色&#xff0c;不通颜色通风方式不通。 我这里的风扇是蓝色&#xff0c;修改…

宠物社区风格 商业版(GBK)Discuz模板

仿爱宠乐园宠物社区风格Discuz模板&#xff0c;商业版&#xff08;GBK&#xff09;Discuz模板。 1、版本支持&#xff1a;discuzx3.0版本&#xff0c;discuzx3.1版本&#xff0c;discuzx3.2版本&#xff0c;discuzx3.3版本&#xff0c;discuzx3.4版本。包括网站首页&#xff0…

记一次docker逃逸漏洞的复现

公众号&#xff1a;掌控安全EDU 分享更多技术文章&#xff0c;欢迎关注一起探讨学习 利用条件 1.Docker Version <18.09.2 2.RunC Version <1.0-rc6 3.攻击者具有容器文件上传权限&管理员使用exec访问容器||攻击者具有启动容器权限 利用原理 这里的问题存在于&#x…

【已解决】Pyecharts折线图,只有坐标轴没有折线数据

【已解决】Pyecharts折线图&#xff0c;只有坐标轴没有折线数据 1、问题复现2、原因3、问题解决 1、问题复现 在做简单的数据通过 Pyecharts 生成折现图的时候&#xff0c;一直只有坐标轴没有折线数据&#xff0c;但是代码一直看不出问题&#xff0c;代码如下&#xff1a; im…

机器人革命:你一定没见过这些全新的机器人技术!

原创 | 文 BFT机器人 01 通过机器人协作推进危险测绘 在危险测绘领域&#xff0c;研究人员开发了一种合作方案&#xff0c;利用地面和空中机器人对污染区域进行危险测绘。该团队通过使用异构覆盖控制技术提高了密度图的质量并降低了误差。与同质替代方案相比&#xff0c;该策…

win10搭建Selenium环境+java+IDEA(3)

这里主要对前面的maven和selenium做补充说明&#xff0c;以及更新一些pom文件下载依赖的问题。 IDEA里面&#xff0c;如果你创建的工程是maven工程文件&#xff0c;那么就会有一个pom.xml文件&#xff0c;可以在这个网站&#xff1a;https://mvnrepository.com/搜索依赖&#…

聚焦酷开科技智能大屏OS Coolita,打造智能推荐服务能力全景

2023年9月18日—22日&#xff0c;科学和教育计算机协会The Association for Computing Machinery&#xff08;ACM&#xff09;在新加坡举办了为期5天的ACM RecSys 2023&#xff0c;云集了各大品牌的科技巨头技术人员&#xff0c;还有中外各大高等学府学者参与其中&#xff0c;共…

EV证书与OV证书的区别

在保护网站和用户数据的过程中&#xff0c;选择适当的SSL证书至关重要。EV&#xff08;Extended Validation&#xff09;证书和OV&#xff08;Organization Validation&#xff09;证书是SSL证书的两种常见类型&#xff0c;它们在验证过程和信任指示方面有着显著的区别。让我们…

除静电离子风嘴的工作原理及应用

除静电离子风嘴是一种常见的除静电设备&#xff0c;它的工作原理是通过产生大量的负离子来中和物体表面的静电电荷&#xff0c;从而达到除静电的目的。 除静电离子风嘴内部装有一个电离器&#xff0c;电离器会将空气中的氧气分子或水分子电离成正、负离子。这些带电的离子在空…

SVN安装教程

SVN安装教程 1. 下载安装2. 汉化3. SVN配置 1. 下载安装 百度网盘下载&#xff1a; 链接&#xff1a;SVN百度网盘下载 2. 汉化 双击汉化包 点击【下一步】 选中【Confiqure TortoiseSVN to use this language】 点击【完成】 返回桌面&#xff0c;任意位置右击&#xff0…