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

| 在这里插入图片描述

  ​ 我们将探讨如何使用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作为参数传递进去。

<CategoryItemclass="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' }"><CategoryItemv-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.xdnf.cn/news/150487.html

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

相关文章

电子地图 | VINS-FUSION | 小觅相机D系列

目录 一、相关介绍 二、VINS-FUSION环境安装及使用 &#xff08;一&#xff09;Ubuntu18.04安装配置 1、Ubuntu下载安装 2、设置虚拟内存&#xff08;可选&#xff09; &#xff08;二&#xff09;VINS-FUSION环境配置 1、ros安装 2、ceres-solver安装 3、vins-fusion…

JavaScript中的map()和forEach()方法有什么区别?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

工信部教考中心:什么是《研发效能(DevOps)工程师》认证,拿到证书之后有什么作用!(上篇)丨IDCF

在计算机行业中&#xff0c;资质认证可以证明在该领域内的专业能力和知识水平。各种技术水平认证也是层出不穷&#xff0c;而考取具有公信力和权威性的认证是从业者的首选。同时&#xff0c;随着国内企业技术实力的提升和国家对于自主可控的重视程度不断提高&#xff0c;国产证…

基于Java的教学评价管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统功能结构图系统ER图具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划…

Flink+Doris 实时数仓

Flink+Doris 实时数仓 Doris基本原理 Doris基本架构非常简单,只有FE(Frontend)、BE(Backend)两种角色,不依赖任何外部组件,对部署和运维非常友好。架构图如下 可以 看到Doris 的数仓架构十分简洁,不依赖 Hadoop 生态组件,构建及运维成本较低。 FE(Frontend)以 Java 语…

用 Pytorch 自己构建一个Transformer

一、说明 用pytorch自己构建一个transformer并不是难事,本篇使用pytorch随机生成五千个32位数的词向量做为源语言词表,再生成五千个32位数的词向量做为目标语言词表,让它们模拟翻译过程,transformer全部用pytorch实现,具备一定实战意义。 二、论文和概要 …

【数据结构--八大排序】之希尔排序

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

STM32--人体红外感应开关

本文主要介绍基于STM32F103C8T6和人体红外感应开关实现的控制算法 简介 人体红外模块选用HC-SR501人体红外传感器&#xff0c;人体红外感应的主要器件为人体热释电红外传感器。人体都有恒定的体温&#xff0c;一般在36~37度&#xff0c;所以会发出特定波长的红外线&#xff0…

Mac上protobuf环境构建-java

参考文献 getting-started 官网pb java介绍 maven protobuf插件 简单入门1 简单入门2 1. protoc编译器下载安装 https://github.com/protocolbuffers/protobuf/releases?page10 放入.zshrc中配置环境变量  ~/IdeaProjects/test2/ protoc --version libprotoc 3.12.1  …

国庆假期作业6

一、ARM的工作模式 1、非特权模式 user模式&#xff1a;非特权模式&#xff0c;大部分任务执行在这种模式 2、特权模式 异常模式&#xff1a; FIQ : 当一个快速&#xff08;fast) 中断产生时将会进入这种模式 IRQ : 当一个通用&#xff08;normal) 中断产生时将会进入这种模式…

中国企业400电话在线申请办理

在当今竞争激烈的商业环境中&#xff0c;企业需要寻求各种方式来提升客户服务和市场竞争力。而拥有一个专属的400电话号码&#xff0c;不仅可以为企业带来更多的商机&#xff0c;还能提升企业形象和客户满意度。本文将介绍如何在线申请办理中国企业400电话&#xff0c;并提供一…

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

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

EV证书与OV证书的区别

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

HDLbits: ece241 2014 q4

module top_module (input clk,input x,output z ); reg [2:0] Q;always(posedge clk)beginQ[0] < Q[0] ^ x;Q[1] < (~Q[1]) & x;Q[2] < (~Q[2]) | x;z < ~(| Q[2:0]); //错误&#xff01;&#xff01;&#xff01;&#xff01;endendmodule 正确答案&#xf…

Java基于SpringBoot的车辆充电桩

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 1、效果演示效果图 技术栈2、 前言介绍&#xff08;完整源码请私聊&#xff09;3、主要技术3.4.1…

当长假来临,如何定向应用AI?科技力量变革您的假日生活!

“今夜月明人尽望&#xff0c;不知秋思落谁家。”中秋国庆的双节组合&#xff0c;让万千中国家庭迎来了难得的团圆欢庆时刻。长达八天的假期已经开启&#xff0c;现在的你是不是已经背上行囊&#xff0c;浪迹远方了呢&#xff1f; &#xff08;金秋时分&#xff0c;假日光景&am…

知识图谱和大语言模型的共存之道

源自&#xff1a;开放知识图谱 “人工智能技术与咨询” 发布 导 读 01 知识图谱和大语言模型的历史 图1 图2 图3 图4 图5 02 知识图谱和大语言模型作为知识库的优缺点 图6 图7 表1 表2 图8 图9 03 知识图谱和大语言模型双知识平台融合 图10 图11 04 总结与展望 声明:公众号转…

地图资源下载工具数据在线、离线查询及数据激活功能

哨兵相关产品&#xff0c;工具提供了表示系统是否为归档离线的信息&#xff01;您可以利用下载[定时重试]功能激活并下载哨兵相关离线产品数据&#xff01;

浅谈电气防火限流式保护器在小型人员密集场所中的应用

摘要&#xff1a;本文通过结合城市中小型人员密集场所的特点和电气防火限流式保护器的功能&#xff0c;阐述了该类筑物预防电气火灾事故的方法。 关键词&#xff1a;小型人员密集场所&#xff1b;电气防火限流式保护器 0&#xff1a;概述 近年来&#xff0c;随着社会经济的不…

从零开始 Spring Cloud 13:分布式事务

从零开始 Spring Cloud 13&#xff1a;分布式事务 1.分布式事务问题 用一个示例项目演示在分布式系统中使用事务会产生的问题。 示例项目的 SQL&#xff1a;seata_demo.sql 示例项目代码&#xff1a;seata-demo.zip 这个示例项目中的微服务的互相调用依赖于 Nacos&#xf…