【学习】若依源码(前后端分离版)之 “ 数据的导入导出功能”

news2025/2/12 23:59:20

大型纪录片:学习若依源码(前后端分离版)之 “ 数据的导入导出功能”

  • 前言
  • 导入功能实现
    • 前端部分
    • 后端部分
  • 导出功能实现
    • 前端部分
    • 后端部分
    • 8月9日补充:
      • 自定义隐藏属性列
      • 导出对象的子列表
  • 结语

前言

在实际开发中,碰到大批量处理的数据一个一个点击增加真的很麻烦,特别是一些同质化比较大数据。这个时候我们往往会选择用导入功能,通过系统提供的Excel模板批量导入来提高效率。导出自然就不用讲了。

来看若依是怎么实现这些功能的吧,这里我将用我自己定义的一个模块来举例说明。为了方便大家理解,我把若依操作手册(导入导出)也贴出来了。

导入功能实现

前端部分

1、导入按钮

      <el-col :span="1.5">
        <el-button
          type="info"
          plain
          icon="el-icon-upload2"
          size="mini"
          @click="handleImport"
          v-hasPermi="['system:user:import']"
        >导入</el-button>
      </el-col>

2、数据导入的模态框

<!-- 单词导入对话框 -->
    <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
      <el-upload
        ref="upload"
        :limit="1"
        accept=".xlsx, .xls"
        :headers="upload.headers"
        :action="upload.url + '?updateSupport=' + upload.updateSupport"
        :disabled="upload.isUploading"
        :on-progress="handleFileUploadProgress"
        :on-success="handleFileSuccess"
        :auto-upload="false"
        drag
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <div class="el-upload__tip text-center" slot="tip">
          <div class="el-upload__tip" slot="tip">
            <el-checkbox v-model="upload.updateSupport" /> 是否更新已经存在的用户数据
          </div>
          <span>仅允许导入xls、xlsx格式文件。</span>
          <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
        </div>
      </el-upload>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitFileForm">确 定</el-button>
        <el-button @click="upload.open = false">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

其中绑定的参数有:

// 单词导入参数
      upload: {
        // 是否显示弹出层(单词导入)
        open: false,
        // 弹出层标题(单词导入)
        title: "",
        // 是否禁用上传
        isUploading: false,
        // 是否更新已经存在的用户数据
        updateSupport: 0,
        // 设置上传的请求头部
        headers: { Authorization: "Bearer " + getToken() },
        // 上传的地址
        url: process.env.VUE_APP_BASE_API + "/Words/words/importData"
      },

如果是自己开发的话,从他原有的系统里拿出来改一些参数也是可以实现这些效果的。当然了,后端也得实现相应的方法。

3、相关的方法

 /** 导入按钮操作 */
    handleImport() {
      this.upload.title = "单词导入";
      this.upload.open = true;
    },
    /** 下载模板操作 */
    importTemplate() {
      this.download('Words/words/importTemplate', {
      }, `小糖的单词导入模板_(。・∀・)ノ゙嗨${new Date().getTime()}.xlsx`)
    },
    // 文件上传中处理
    handleFileUploadProgress(event, file, fileList) {
      this.upload.isUploading = true;
    },
    // 文件上传成功处理
    handleFileSuccess(response, file, fileList) {
      this.upload.open = false;
      this.upload.isUploading = false;
      this.$refs.upload.clearFiles();
      this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
      this.getList();
    },
    // 提交上传文件
    submitFileForm() {
      this.$refs.upload.submit();
    }

4、效果展示
图一

后端部分

1、我们来看“下载模板”的功能是怎么实现的,根据先看相关代码

**在实体变量上添加@Excel注解**

/** 单词ID */
    @Excel(name = "单词ID(更新数据需要)")
    private Long wId;

    /** 音标 */
    @Excel(name = "音标")
    private String wSymbol;

    /** 单词 */
    @Excel(name = "单词")
    private String wName;

    /** 单词难度(分五级) */
    @Excel(name = "单词难度(1-5)", readConverterExp = "1=一级,2=二级,3=三级,4=四级,5=五级")
    private Integer wDifficultly;

    /** 备注 */
    @Excel(name = "备注")
    private String wNote;
    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response)
    {
        ExcelUtil<Words> util = new ExcelUtil<Words>(Words.class);
        util.importTemplateExcel(response, "用户数据");
    }

    /**
     * 对list数据源将其里面的数据导入到excel表单
     * 
     * @param sheetName 工作表的名称
     * @param title 标题
     * @return 结果
     */
    public void importTemplateExcel(HttpServletResponse response, String sheetName, String title)
    {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        this.init(null, sheetName, title, Type.IMPORT);
        exportExcel(response);
    }

这个方法的意思是,它用于下载单词数据的导入模板,也就是一个空白的Excel文件,里面只有表头,方便用户填写数据并上传。具体的步骤如下:

  • 首先,它创建了一个ExcelUtil对象,这是若依封装的一个工具类,用于操作Excel文件。它传入了Words.class作为参数,表示要导入的实体类类型是系统用户。
  • 然后,它调用了ExcelUtil对象的importTemplateExcel方法,这个方法会根据实体类的@Excel注解生成表头,并将Excel文件写入到HttpServletResponse对象中。它传入了response作为参数,表示要写入的响应对象,和"用户数据"作为参数,表示要生成的Excel文件的名称。
  • 最后,当用户访问这个接口时,他们就可以下载一个名为"用户数据.xlsx"的导入模板文件,里面只有一行表头,例如"用户ID",“用户名”,"密码"等。用户可以在这个文件中填写自己的数据,并通过另一个接口上传到系统中。

接下来我举一个简单的例子来解释一下:

假设你有一个网站,你想让用户可以下载一些文件,比如图片,音乐,文档等。你怎么做呢?

你可能会在网站上放一个链接,比如"点击这里下载图片",当用户点击这个链接时,他们的浏览器就会打开一个对话框,让他们选择保存文件的位置和名称。这个过程就是将文件写入到HttpServletResponse对象中。

HttpServletResponse对象是一个Java类,它表示服务器给客户端(也就是用户的浏览器)发送的响应。它包含了响应的内容,类型,状态码等信息。

当你想让用户下载一个文件时,你需要设置响应的类型为application/vnd.ms-excel,这表示响应的内容是一个Excel文件。你还需要设置响应的头部,比如Content-Disposition,这表示响应的内容是一个附件,而不是直接显示在浏览器中。你还可以指定附件的名称,比如"用户数据.xlsx"。

然后,你需要使用EasyExcel这个工具类来生成一个Excel文件,并将它写入到HttpServletResponse对象的输出流中。输出流是一个通道,它可以将数据从服务器发送到客户端。当你把Excel文件写入到输出流中时,客户端就会收到这个文件,并弹出对话框让用户保存。

所以,这一段话的意思就是:若依使用了EasyExcel这个工具类来生成一个Excel文件,并将它写入到HttpServletResponse对象中。它传入了response作为参数,表示要写入的响应对象,和"用户数据"作为参数,表示要生成的Excel文件的名称。

2、现在我们可以来看“导入数据”的功能是怎么实现的:

    @Log(title = "单词管理", businessType = BusinessType.IMPORT)
    @PreAuthorize("@ss.hasPermi('system:user:import')")
    @PostMapping("/importData")
    public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
    {
        ExcelUtil<Words> util = new ExcelUtil<Words>(Words.class);
        List<Words> wordsList = util.importExcel(file.getInputStream());
        String operName = getUsername();
        System.out.println(wordsList);
        String message = wordsService.importWord(wordsList, updateSupport, operName);
        return AjaxResult.success(message);
    }

重点看一下实现类wordsService.importWord方法

/**
     * 导入单词数据
     *
     * @param wordsList 单词数据列表
     * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
     * @param operName 操作用户
     * @return 结果
     */
    @Override
    public String importWord(List<Words> wordsList, Boolean isUpdateSupport, String operName)
    {
        if (StringUtils.isNull(wordsList) || wordsList.size() == 0)
        {
            throw new ServiceException("导入单词数据不能为空!");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        for (Words words : wordsList)
        {
            System.out.println(words);
            try
            {
                // 验证是否存在这个单词
                Words u = wordsMapper.selectWordsByWName(words.getwName());
                if (StringUtils.isNull(u))
                {
                    successNum++;
                    this.insertWords(words);
                    successMsg.append("<br/>" + successNum + "、单词 " + words.getwName() + " 导入成功");
                }
                else if (isUpdateSupport)
                {
                    words.setUpdateBy(operName);
                    this.updateWords(words);
                    successNum++;
                    successMsg.append("<br/>" + successNum + "、单词 " + words.getwName() + " 更新成功");
                }
                else
                {
                    failureNum++;
                    failureMsg.append("<br/>" + failureNum + "、单词 " + words.getwName() + " 已存在");
                }
            }
            catch (Exception e)
            {
                failureNum++;
                String msg = "<br/>" + failureNum + "、单词 " + words.getwName() + " 导入失败:";
                failureMsg.append(msg + e.getMessage());
                log.error(msg, e);
            }
        }
        if (failureNum > 0)
        {
            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
            throw new ServiceException(failureMsg.toString());
        }
        else
        {
            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
        }
        return successMsg.toString();
    }

这部分代码定义了一个名为importUser的方法,用于导入用户数据。具体的步骤如下:

  • 首先,它接收了三个参数:
    • wordsList: 这是一个Words类型的列表,表示要导入的用户数
    • isUpdateSupport: 这是一个Boolean类型的值,表示是否支持更新已存在的用户数据。
    • operName: 这是一个String类型的值,表示操作用户的名称。
  • 然后,它判断了userList是否为空或者长度为0,如果是,则抛出一个ServiceException异常,提示"导入用户数据不能为空!"。
  • 接着,它定义了四个变量原来表示导入成功、失败的数量,和成功、失败的信息。
  • 接着,它遍历了wordsList中的每个words对象,执行以下操作:
    • 首先,它使用wordsMapper中的selectWordsByWName方法,根据Words对象的wName属性查询数据库中是否存在,并将结果赋给了u变量。这是一个Words类型的对象,如果不存在则为null。
    • 然后,它判断了u是否为null,如果是,则表示这个单词不存在,则去创建这个单词。
    • 否则,如果u不为null,则表示这个用户已存在,则判断isUpdateSupport是否为true,如果是,则表示支持更新已存在的数据;如果否,则表示不支持更新已存在的用户数据。
  • 最后,它判断了failureNum是否大于0,如果是,则表示有导入失败的数据,这会终止方法的执行,并返回错误信息给调用者;如果failureNum不大于0,则表示没有导入失败的用户数据,则正常结束方法的执行,并返回成功信息给调用者。

以上就是导入功能的整套流程了,接下来来看导出部分。


导出功能实现

前端部分

1、导出按钮

<el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="el-icon-download"
          size="mini"
          @click="handleExport"
          v-hasPermi="['Words:words:export']"
        >导出</el-button>
      </el-col>

2、导出请求方法(参考如下)

// 导出接口exportUser
import { exportUser } from "@/api/system/user";

/** 导出按钮操作 */
    handleExport() {
      this.download('Words/words/export', {
        ...this.queryParams
      }, `导出单词_q(≧▽≦q)${new Date().getTime()}.xlsx`)
    },

后端部分

1、导出按钮的请求就对应该方法

    /**
     * 导出单词实体类列表
     */
    @PreAuthorize("@ss.hasPermi('Words:words:export')")
    @Log(title = "单词实体类", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, Words words)
    {
        List<Words> list = wordsService.selectWordsList(words);
        ExcelUtil<Words> util = new ExcelUtil<Words>(Words.class);
        util.exportExcel(response, list, "单词实体类数据");
    }

2、service层实现方法

    /**
     * 根据条件分页查询未分配用户角色列表
     *
     * @param user 用户信息
     * @return 用户信息集合信息
     */
        /**
     * 查询单词实体类列表
     * 
     * @param words 单词实体类
     * @return 单词实体类
     */
    @Override
    public List<Words> selectWordsList(Words words)
    {
        return wordsMapper.selectWordsList(words);
    }


稍微解释一下吧:

  • 首先,它使用了@PreAuthorize这个注解,表示这个方法需要有"Words:words:export"这个权限才能执行。这是一个Spring Security的功能,用于控制访问权限。
  • 然后,它使用了@Log这个注解,表示这个方法的执行会被记录到日志中。
  • 接着,它调用了wordsService中的selectWordsList方法,传入了words对象作为参数,查询数据库中符合条件的单词实体类列表,并将结果赋给了list变量。这是一个Words类型的列表,表示要导出的数据。
  • 然后,它创建了一个ExcelUtil对象,这是若依封装的一个工具类,用于操作Excel文件。它传入了Words.class作为参数,表示要导出的实体类类型是单词实体类。
  • 最后,它调用了ExcelUtil对象的exportExcel方法,传入了三个参数:
    • response: 这是一个HttpServletResponse类型的对象,表示要写入的响应对象。
    • list: 这是一个Words类型的列表,表示要导出的数据。
    • “单词实体类数据”: 这是一个字符串,表示要生成的Excel文件的名称。

这样,当用户调用这个方法时,它就会根据查询条件从数据库中获取单词实体类列表,并将其导出为一个Excel文件,并写入到响应对象中。用户就可以下载这个文件,并查看其中的数据。


8月9日补充:

自定义隐藏属性列

有时候我们希望对列信息根据业务去动态显示,那么我们可以进行如下处理。

示例:对用户进行条件判断,符合条件则隐藏属性。导出的文件则不会显示此列信息。

@PostMapping("/export")
public void export(HttpServletResponse response, SysUser user)
{
	List<SysUser> list = userService.selectUserList(user);
	ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
	if (条件A) {
	  // 不显示用户ID(单个)
	  util.hideColumn("userId");
	} else if (条件B) {
	  // 不显示用户名称、用户手机(多个)
	  util.hideColumn("userId", "phonenumber");
	} } else if (条件C) {
	  // 不显示用户邮箱、部门名称(子对象)
	  util.hideColumn("email", "dept.deptName");
	}
	util.exportExcel(response, list, "用户数据");
}

导出对象的子列表

有时候对象里面还包含集合列表,例如用户管理包含多个角色需要导出,那么我们可以进行如下处理。

SysUser.java

public class SysUser
{
    @Excel(name = "用户编号", cellType = ColumnType.NUMERIC, width = 20, needMerge = true)
    private String userId;

    @Excel(name = "用户名称", width = 20, needMerge = true)
    private String userName;

    @Excel(name = "邮箱", width = 20, needMerge = true)
    private String email;

    @Excel(name = "角色")
    private List<SysRole> roles;

    public String getUserId()
    {
        return userId;
    }

    public void setUserId(String userId)
    {
        this.userId = userId;
    }

    public String getUserName()
    {
        return userName;
    }

    public void setUserName(String userName)
    {
        this.userName = userName;
    }

    public String getEmail()
    {
        return email;
    }

    public void setEmail(String email)
    {
        this.email = email;
    }

    public List<SysRole> getRoles()
    {
        return roles;
    }

    public void setRoles(List<SysRole> roles)
    {
        this.roles = roles;
    }
}

SysRole.java

public class SysRole
{
    @Excel(name = "角色编号", cellType = ColumnType.NUMERIC)
    private String roleId;

    @Excel(name = "角色名称")
    private String roleName;

    @Excel(name = "角色字符")
    private String roleKey;

    public String getRoleId()
    {
        return roleId;
    }

    public void setRoleId(String roleId)
    {
        this.roleId = roleId;
    }

    public String getRoleName()
    {
        return roleName;
    }

    public void setRoleName(String roleName)
    {
        this.roleName = roleName;
    }

    public String getRoleKey()
    {
        return roleKey;
    }

    public void setRoleKey(String roleKey)
    {
        this.roleKey = roleKey;
    }

}

效果为:
图不知道多少

结语

可能牵扯到更深的代码讲的不是很清楚,如有疑问多多评论留言,一起探讨交流吧。

那么以上就是唐某的一些理解。这次的分享就到这里了。记得一键三连~( •̀ ω •́ )✧

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

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

相关文章

【java实习评审】对推电影详情模块的基本电影模型设计到位,并能考虑到特色业务的设计

大家好&#xff0c;本篇文章分享一下【校招VIP】免费商业项目"推电影"第一期 电影详情模块 Java同学的开发文档周最佳作品。该同学来自暨南大学电子信息专业。 本项目的商业出发点&#xff1a; 豆瓣评分越来越水&#xff0c;不太符合年青人的需求&#xff0c;我们推…

React Dva项目小优化之redux-action

之前 我们讲过 models 接下啦 我们来给大家讲一个新的库 这个库的话 有最好 没有影响也不大 它主要是帮助我们处理 action的 我们直接在 GitHub 官网上搜索 redux-action 我们搜出来 第一个就是 从星数来看 还是非常优秀的 我们拉下来 找到这个Documentation 然后点击进去 进…

【WiFi】WiFi 6E最新支持的国家和频段

信道Map图 国家和频段 CountryStatus Spectrum Andorra Adopted Considering 5945-6425 MHz 6425-7125 MHz ArgentinaAdopted5925-7125 MHzAustralia Adopted Considering 5925-6425 MHz 6425-7125 MHz Austria Adopted Considering 5945-6425 MHz 6425-7125 MHz BahrainA…

IOC容器 - Autofac

DI&#xff08;依赖注入&#xff09;&#xff1a;DI&#xff08;Dependency Injection&#xff09;是一种实现松耦合和可测试性的软件设计模式。它的核心思想是将依赖关系的创建与管理交给外部容器&#xff0c;使得对象之间只依赖于接口而不直接依赖于具体实现类。通过依赖注入…

MySQL多表连接查询3

目录 表结构 创建表 表数据 查询需求&#xff1a; 1.查询student表的所有记录 2.查询student表的第2条到4条记录 3.从student表查询所有学生的学号&#xff08;id&#xff09;、姓名&#xff08;name&#xff09;和院系&#xff08;department&#xff09;的信息 4.从s…

【boost网络库从青铜到王者】第一篇:visual studio 系列配置boost网络库

文章目录 1、asio网络库的介绍2、asio网络库的下载和编译3、visual studio 配置boost头文件以及库文件4、使用boost网络库来写一段代码测试 1、asio网络库的介绍 ASIO&#xff08;Asynchronous I/O&#xff09; 是一个C网络库&#xff0c;由Boost C 库的一部分衍生而来&#x…

PHP 求解两字符串所有公共子序列及最长公共子序列 支持多字节字符串

/*** 获取两字符串所有公共子序列【不连续的】 例&#xff1a;abc ac > ac** param string $str1 字符串1* param string $str2 字符串2** return array*/ function public_sequence(string $str1, string $str2): array {$data [[-1, -1, , 0, ]]; // 子序列容器【横坐标 …

project_serial_port

文章目录 效果![在这里插入图片描述](https://img-blog.csdnimg.cn/77ab9856af714b06b69f2ca959dc8ea3.png)源码widget.cppwidget.h 效果 源码 widget.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent)…

Java学习内容有什么?这篇全是干货

Java 是目前应用非常广泛的一种计算机高级编程语言&#xff0c;它是一种计算机程序设计语言&#xff0c;被广泛应用于各大行业和领域&#xff0c;如金融、汽车、医疗、电子、互联网等等。Java 诞生之后风靡全球&#xff0c;并且一直保持其高人气&#xff0c;因此 Java 的学习也…

自动清洁离子风机一般具有什么特点?

自动清洁离子风机是一种能够自动清洁空气中的离子&#xff0c;并同时进行空气净化的设备。它采用先进的离子生成技术&#xff0c;可以释放负离子并吸附空气中的污染物&#xff0c;如细菌、病毒、灰尘、花粉等&#xff0c;将它们沉积在设备表面。当设备侦测到需要清洁时&#xf…

那些年的Xposed开发经验记录

把之前写的Xposed相关文章合并到一块&#xff0c;方便查阅 目录 多进程App的Hook问题XposedHelper中的静态变量demo的AndroidManifest.xml的测试核心代码结论限制handleLoadPackage被单个进程多次执行的问题 多dex Hook问题为应用增加权限利用Xposed删除权限参考 Hook框架集锦…

《漫画算法:小灰的算法之旅》——赠书活动

我想应该有很多人对我今天推荐的书籍不陌生&#xff0c;《漫画算法&#xff1a;小灰的算法之旅》已经是圈内人熟知的“红人”了&#xff0c;但也存在不断有新人入坑&#xff0c;这里就好好介绍一下这本包上“糖衣”的算法“炮弹”吧&#xff0c;整个过程如同本书形象“Q弾可爱&…

热成像技术创新,助力人工智能炼就黑夜中的火眼金睛

原创 | 文 BFT机器人 普渡大学&#xff08;Purdue University&#xff09;的研究人员利用他们正在申请专利的方法来改进传统的机器视觉和感知&#xff0c;从而推动机器人技术和自动控制领域的发展。 埃尔莫尔家族电气与计算机工程学院&#xff08;Elmore Family School of Ele…

Git使用教程(看完会了也懂了)

本文简单的记录一下Git的一些基本的概念和基础的操作&#xff0c;主要是弄懂基本的东西&#xff0c;能够快速的上手并开始使用&#xff1b;但是更多深层的原理和应用还是需要另外去进一步学习的。 创建版本库 我们先整一个git仓库&#xff0c;再来看看基本概念&#xff1b; 新…

免费插件-illustrator-Ai插件-印刷功能-二维码生成

文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.示例5.1.QR常用二维码5.2.PDF4175.3.EAN13 6.总结 1.介绍 本文介绍一款免费插件&#xff0c;加强illustrator使用人员工作效率&#xff0c;进行二维码生成。首先从下载网址下载这款插件 https://download.csd…

国际顶级学术会议ISSTA召开,中山大学与微众银行联合发表区块链最新研究成果

美国当地时间7月17日&#xff0c;软件工程领域顶级会议ISSTA 2023在西雅图正式召开。ISSTA &#xff08;The 32nd ACM SIGSOFT International Symposium on Software Testing and Analysis &#xff09;是软件测试与分析方面最著名的国际会议之一&#xff0c;也是中国计算机学会…

中国钢铁工业协会 :2022年钢铁行业经济运行报告(附下载)

关于报告的所有内容&#xff0c;公众【营销人星球】获取下载查看 核心观点 2022年&#xff0c;我国粗钢产量10.18亿吨&#xff0c;比上年下降1.7%&#xff0c;连续两年下降&#xff0c;降幅比上年收窄。2022年&#xff0c;出口钢材 6732万吃&#xff0c;比上年增长0.9%;进口钢…

RabbitMQ:可靠消息传递的强大消息中间件

消息中间件在现代分布式系统中起着关键作用&#xff0c;它们提供了一种可靠且高效的方法来进行异步通信和解耦。在这篇博客中&#xff0c;我们将重点介绍 RabbitMQ&#xff0c;一个广泛使用的开源消息中间件。我们将深入探讨 RabbitMQ 的特性、工作原理以及如何在应用程序中使用…

javaScript:文档流写入和元素写入

目录 前言 文档流写入 把元素直接写入到文档流 注意​编辑 注意 元素写入 注意 innerHTML 特点&#xff1a; 设置内容 获取内容 innerText 特点&#xff1a; 注意 相关代码 前言 在JavaScript中&#xff0c;文档流写入是指将内容直接写入到DOM&#xff08;文档对…

用户生成内容(UGC)与海外网红营销:激活品牌传播的力量

互联网和社交媒体的迅速发展为品牌营销带来了前所未有的机遇。用户生成内容&#xff08;UGC&#xff09;是指由普通用户自发创作、分享和发布的各种内容&#xff0c;而海外网红是指在海外社交平台上拥有大量粉丝和影响力的人物。这两者结合&#xff0c;为品牌传播带来了强大的推…