【RuoYi】框架中使用wangdietor富文本编辑器

news2024/12/23 13:16:20

一、前言

  在上篇博客中,介绍了RuoYi中如何实现文件的上传与下载,那么这篇博客讲一下如何在RuoYi中使用富文本编辑器,这部分的内容是向B站程序员青戈学习的,当然我这里就会把学到的内容做一个总结,当然也会说一下在RuoYi中使用富文本编辑器会出现的一些问题。希望对大家有所帮助!

二、准备工作

1、安装wangdietor

npm install @wangeditor/editor --save

2、导入相关的包

b641e9bfbc0d4e4e98f456cebdc7d4fa.png

三、Vue代码

  首先我想说一下使用这个wangdietor富文本编辑器的主要思路:先对需要使用wangdietor的字段绑定标签id=''editor",然后使用我们自己写的setRichText()函数对编辑器进行初始化和对一些图片、视频上传的接口进行自定义;最后要将富文本编辑器的内容存在数据中。当然数据显示的时候可能会有一些标签,此时我们可以通过按钮,出现弹窗来规避这种情况。

核心代码如下:

    setRichText(){
      this.$nextTick( () => {
        this.editor=new E('#editor')//创建对象
        this.editor.highlight = hljs//代码高亮
        this.editor.config.uploadImgServer = process.env.VUE_APP_BASE_API+'/file/editor/upload'//图片上传的接口
        this.editor.config.uploadFileName = 'file'
        this.editor.config.uploadImgParams = {
          type:"img"
        }
        this.editor.config.uploadVideoServer = process.env.VUE_APP_BASE_API+"/file/editor/upload"//视频上传的接口
        this.editor.config.uploadVideoName = 'file'
        this.editor.config.uploadVideoParams = {
          type:"video"
        }
        this.editor.create()
      })
    }

四、Java代码

后端代码主要就是在图片和视频的上传了,下面是wangdietor规定的图片和视频的上传的格式

04122df7a4e3464dae283163aebec9e4.png

d5df27bfc7354a20926dfa74794a5214.png

核心代码如下:

@RestController
@Api(tags = "文件上传与下载模块")
@RequestMapping("/file")
public class FileController extends BaseController {
    @Value("${ip:localhost}")
    String ip;//通过配置文件拿到ip

    @Value("${server.port}")
    String port;

    private static final String ROOT_PATH = System.getProperty("user.dir")+ File.separator+"files";//文件根目录

    @PostMapping("/editor/upload")
    public Dict editorUpload(MultipartFile file,@RequestParam String type) throws IOException {
        System.out.println(6767);
        String originalFilename = file.getOriginalFilename();
        String mainName = FileUtil.mainName(originalFilename);
        String extName = FileUtil.extName(originalFilename);
        if(!FileUtil.exist(ROOT_PATH)){
            FileUtil.mkdir(ROOT_PATH);
        }
        if(FileUtil.exist(ROOT_PATH+File.separator+originalFilename)){
            originalFilename=System.currentTimeMillis()+"_"+mainName+"."+extName;
        }
        File saveFile = new File(ROOT_PATH + File.separator + originalFilename);
        file.transferTo(saveFile);
        String url ="http://"+ip+":"+port+"/file/download/"+originalFilename;
        if("img".equals(type)){//上传图片
            return Dict.create().set("errno",0).set("data", CollUtil.newArrayList(Dict.create().set("url",url)));
        }else if("video".equals(type)){
            return Dict.create().set("errno",0).set("data", Dict.create().set("url",url));
        }
        return Dict.create().set("errno",0);
    }
    @ApiOperation(value = "文件下载")
    @GetMapping("/download/{fileName}")
    public void download(@PathVariable String fileName, HttpServletResponse response) throws IOException {
//        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName,"UTF-8"));//文件下载
//        response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileName,"UTF-8"));//文件预览
        String filePath = ROOT_PATH+File.separator+fileName;//拿到文件的路径
        if(!FileUtil.exist(filePath)){//文件不存在,就不用管,因为没有东西可写
            return;
        }
        byte[] bytes = FileUtil.readBytes(filePath);//使用FileUtil从filePath中去读取文件
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(bytes);//把文件的字节数写出去
        outputStream.flush();//刷新一下
        outputStream.close();//一定要关闭文件流
    }
}

后端报错

36161883564d40b294ff46ed8744776d.png

如果出现这个错误,我们改一下对应的XSS中的内容即可

96af8f9a98a14da199f58646d2906f9f.png

效果图

8037f5046f5e4c18991e6aec5cee3a95.png

630e26f6c6ce4b90b7a63b872b4830c8.png

bd893b8b814346cdbc83cecae86c687f.png

e7f720e32f88481a955aa3734da9857a.png

全部代码

  以下的代码是包含了上述效果图中的所有代码,至于一些按钮、弹窗的设计、数据的请求与显示在该代码中都有,都是一些比较小的点,我就不详细介绍了,大家感兴趣的话就在评论区评论就好了,我看到了都会回复的。

<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="学号" prop="stuNum">
        <el-input
          v-model="queryParams.stuNum"
          placeholder="请输入学号"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="姓名" prop="stuName">
        <el-input
          v-model="queryParams.stuName"
          placeholder="请输入姓名"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleAdd"
          v-hasPermi="['system:student:add']"
        >新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          plain
          icon="el-icon-edit"
          size="mini"
          :disabled="single"
          @click="handleUpdate"
          v-hasPermi="['system:student:edit']"
        >修改</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['system:student:remove']"
        >删除</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="el-icon-download"
          size="mini"
          @click="handleExport"
          v-hasPermi="['system:student:export']"
        >导出</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table v-loading="loading" :data="studentList" @selection-change="handleSelectionChange" @close="closeDialog()">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="表编号" align="center" prop="tableId" />
      <el-table-column label="学号" align="center" prop="stuNum" />
      <el-table-column label="姓名" align="center" prop="stuName" />
      <el-table-column label="性别" align="center" prop="stuSex" />
      <el-table-column label="爱好" align="center" prop="stuHobby" >
        <template v-slot="scope">
          <el-button type="primary" size="mini" @click="handleStuHobbyContent(scope.row)">显示</el-button>
        </template>
      </el-table-column>
      <el-table-column label="家庭信息">
        <template slot-scope="scope">
          <el-button type="success" size="mini" @click="handleJump(scope.row)">按钮</el-button>
        </template>
      </el-table-column>
      <el-table-column label="头像上传">
        <template v-slot="scope">
          <el-upload
            action="http://localhost:8080/file/upload"
            :show-file-list="false"
            :on-success="(row,file,filelist) => handleTableFileUpload(scope.row,file,filelist)"
          >
            <el-button slot="trigger" size="mini" type="primary">选取文件</el-button>
          </el-upload>
        </template>
      </el-table-column>
      <el-table-column label="头像" align="center" prop="stuAvatar" >
        <template v-slot="scope">
          <el-image v-if="scope.row.stuAvatar" :src="scope.row.stuAvatar" style="with:50px;height: 50px"></el-image>
          <div>
            <el-button type="success" size="mini" @click="preview(scope.row.stuAvatar)">预览</el-button>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['system:student:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['system:student:remove']"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 添加或修改学生信息列表对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="60%" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="学号" prop="stuNum">
          <el-input v-model="form.stuNum" placeholder="请输入学号" />
        </el-form-item>
        <el-form-item label="姓名" prop="stuName">
          <el-input v-model="form.stuName" placeholder="请输入姓名" />
        </el-form-item>
        <el-form-item label="性别" prop="stuSex">
          <el-input v-model="form.stuSex" placeholder="请输入性别" />
        </el-form-item>
        <el-form-item label="爱好" prop="stuHobby" class="w-e-text">
          <div id="editor"></div>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
    <div style="margin:10px 0">
      <el-upload
        class="upload-demo"
        drag
        action="http://localhost:8080/file/upload"
        :on-success="handleMultipleUpload"
        multiple>
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
      </el-upload>
    </div>

    <el-dialog title="爱好" :visible.sync="open1" width="60%" append-to-body :close-on-click-modal="false">
      <el-card class="w-e-text" >
        <div v-html="stuHobbyContent"></div>
      </el-card>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="open1 = false">确 定</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>
import { listStudent, getStudent, delStudent, addStudent, updateStudent } from "@/api/system/student";
import E from "wangeditor"
import hljs from "highlight.js"
export default {
  name: "Student",
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 学生信息列表表格数据
      studentList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        stuNum: null,
        stuName: null,
      },
      // 表单参数
      form: {},
      // 表单校验
      rules: {
        stuNum: [
          { required: true, message: "学号不能为空", trigger: "blur" }
        ],
        stuName: [
          { required: true, message: "姓名不能为空", trigger: "blur" }
        ],
      },
      urls:'',
      editor:null,
      open1:false,
      stuHobbyContent:''
    };
  },
  created() {
    this.getList();
  },
  methods: {
    handleStuHobbyContent(row){
      this.stuHobbyContent=row.stuHobby;
      this.open1=true;
    },
    setRichText(){
      this.$nextTick( () => {
        this.editor=new E('#editor')
        this.editor.highlight = hljs
        this.editor.config.uploadImgServer = process.env.VUE_APP_BASE_API+'/file/editor/upload'
        this.editor.config.uploadFileName = 'file'
        this.editor.config.uploadImgParams = {
          type:"img"
        }
        this.editor.config.uploadVideoServer = process.env.VUE_APP_BASE_API+"/file/editor/upload"
        this.editor.config.uploadVideoName = 'file'
        this.editor.config.uploadVideoParams = {
          type:"video"
        }
        this.editor.create()//
      })
    },
    closeDialog() {
      this.editor.destroy()
      this.editor = null
    },
    handleMultipleUpload(response,file,fileList){
      this.urls=fileList.map(v => v.response?.msg)
      console.log(this.urls)//拿到我们的图片的路径
    },
    preview(url){
      window.open(url)
    },
    handleTableFileUpload(row,file,fileList){
      console.log(row,file)
      row.stuAvatar=file.response.msg//注意我们使用的是RuoYi框架中的AjaxResult结果集,返回的数据是在response下的msg中
      //触发更新
      //使用RuoYi中封装好的updateStudent()函数,将当前对象更新
      updateStudent(row).then(response => {
        if(response.code=='200'){
          this.$message.success("上传成功")
        }else{
          this.$message.success("上传失败")
        }
        this.form = response.data;
      });
    },
    /** 查询学生信息列表列表 */
    getList() {
      this.loading = true;
      listStudent(this.queryParams).then(response => {
        this.studentList = response.rows;
        this.total = response.total;
        this.loading = false;
      });
    },
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        tableId: null,
        stuNum: null,
        stuName: null,
        stuSex: null,
        stuHobby: null,
        stuAvatar: null,
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.tableId)
      this.single = selection.length!==1
      this.multiple = !selection.length
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加学生信息列表";
      this.setRichText();
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      this.setRichText();
      setTimeout(()=>{//延时加载
        this.editor.txt.html(row.stuHobby)
      },0);
      const tableId = row.tableId || this.ids
      getStudent(tableId).then(response => {
        this.form = response.data;
        this.open = true;
        this.title = "修改学生信息列表";
      });
    },
    /** 页面跳转按钮操作 */
    handleJump(row){
      const tableId = row.tableId || this.ids
      getStudent(tableId).then(response => {
        this.$router.push("/student/family-data/index/"+tableId)
      });
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          let content = this.editor.txt.html();
          this.form.stuHobby=content;
          if (this.form.tableId != null) {

            updateStudent(this.form).then(response => {
              this.$modal.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            });
          } else {
            addStudent(this.form).then(response => {
              this.$modal.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            });
          }
        }
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const tableIds = row.tableId || this.ids;
      this.$modal.confirm('是否确认删除学生信息列表编号为"' + tableIds + '"的数据项?').then(function() {
        return delStudent(tableIds);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("删除成功");
      }).catch(() => {});
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download('system/student/export', {
        ...this.queryParams
      }, `student_${new Date().getTime()}.xlsx`)
    }
  }
};
</script>

五、总结

  学无止尽,大家加油哦!希望我们都能在顶峰想见!

 

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

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

相关文章

Java基础语法---集合---ArrayList

ArrayList是什么 ArrayList可以看作是一个动态数组&#xff0c;提供了自动扩容的能力&#xff0c;意味着它能够根据需要自动调整其大小以容纳更多的元素&#xff0c;而无需预先指定数组的容量。 使用ArrayList需要加入包 import java.util.ArryList ArrayList与普通数组的不同…

【C++】─篇文章带你熟练掌握 map 与 set 的使用

目录 一、关联式容器二、键值对三、pair3.1 pair的常用接口说明3.1.1 [无参构造函数](https://legacy.cplusplus.com/reference/utility/pair/pair/)3.1.2 [有参构造函数 / 拷贝构造函数](https://legacy.cplusplus.com/reference/utility/pair/pair/)3.1.3 [有参构造函数](htt…

bootstrap5-学习笔记1-容器+布局+按钮+工具

参考&#xff1a; Bootstrap5 教程 | 菜鸟教程 https://www.runoob.com/bootstrap5/bootstrap5-tutorial.html Spacing Bootstrap v5 中文文档 v5.3 | Bootstrap 中文网 https://v5.bootcss.com/docs/utilities/spacing/ 之前用bootstrap2和3比较多&#xff0c;最近用到了5&a…

【FAS】《Liveness Detection on Face Anti-spoofing》

文章目录 原文总结与评价CNN-RNN vs 三维卷积作者的方法 原文 [1]欧阳文汉.反人脸图像欺诈的活体识别方法研究[D].浙江大学,2020.DOI:10.27461/d.cnki.gzjdx.2020.002675. 总结与评价 时序运动信息与传统的空间纹理信息相结合 基于相位平移的运动放大算法不错 视觉大小细胞…

每日一题《leetcode--LCR 029.循环有序列表的插入》

https://leetcode.cn/problems/4ueAj6/ 这道题整体上想插入数据有三种情况&#xff1a; 1、整个列表是空列表&#xff0c;需要返回插入的结点 2、整个列表只有一个结点&#xff0c;需要在头结点后插入新结点&#xff0c;随机把新结点的next指向头结点 3、整个列表的结点 >1 …

关于飞速(FS)800G光模块的技术问答

随着云计算、物联网&#xff08;IoT&#xff09;和大数据等技术的快速发展&#xff0c;对网络带宽和传输速率的需求越来越大。飞速&#xff08;FS&#xff09;800G光模块的引入旨在满足对高速数据传输的需求&#xff0c;该光模块采用先进的调制解调技术和高密度光电子元件&…

STM32F103VE和STM32F407VE的引脚布局

STM32F103VE vs STM32F407VE 引脚对比表 引脚 STM32F103VE STM32F407VE 备注 1 VSS VSS 地 2 VDD VDD 电源 3 VSSA VSSA 模拟地 4 VDDA VDDA 模拟电源 5 OSC_IN OSC_IN 外部时钟输入 6 OSC_OUT OSC_OUT 外部时钟输出 7 NRST NRST 复位 8 PC13 (GPIO) PC13 (GPIO) GPIO 9 PC14 (…

鸿蒙全栈开发-一文读懂鸿蒙同模块不同模块下的UIAbility跳转详解

前言 根据第三方机构Counterpoint数据&#xff0c;截至2023年三季度末&#xff0c;HarmonyOS在中国智能手机操作系统的市场份额已经提升至13%。短短四年的时间&#xff0c;HarmonyOS就成长为仅次于安卓、苹果iOS的全球第三大操作系统。 因此&#xff0c;对于鸿蒙生态建设而言&a…

依赖注入方式和自动加载原理

依赖注入 Spring提供了依赖注入的功能&#xff0c;方便我们管理和使用各种Bean&#xff0c;常见的方式有&#xff1a; 字段注入&#xff08;Autowired 或 Resource&#xff09;构造函数注入set方法注入 在以往代码中&#xff0c;我们经常利用Spring提供的Autowired注解来实现…

C语言操作UTF-8编码和GBK编码的文件的示例

一、基本介绍 在C语言中&#xff0c;处理文件编码&#xff08;如UTF-8或GBK&#xff09;时&#xff0c;需要注意C标准库中的文件操作函数&#xff08;如fopen, fread, fwrite, fclose等&#xff09;并不直接支持Unicode或特定字符集的编码。这些函数通常处理字节流&#xff0c…

FreeRTOS基础(十一):消息队列

本文将详细全方位的讲解FreeRTOS的消息队列&#xff0c;其实在FreeRTOS中消息队列的重要性也不言而喻&#xff0c;与FreeRTOS任务调度同等重要&#xff0c;因为后面的各种信号量基本都是基于消息队列的。 目录 一、消息队列的简介 1.1 产生的原因 1.2 消息队列的解决办法 …

防病毒克星——白名单可信系统

白名单作为一种网络安全措施&#xff0c;其核心概念在于限制用户只能访问网络所有者定义的受信任内容。这种机制在保护系统免受恶意软件、病毒等攻击方面发挥着重要作用。然而&#xff0c;关于白名单是否可以防病毒的问题&#xff0c;实际上涉及了多个方面的考虑。 首先&#x…

B=2W,奈奎斯特极限定理详解

一直没搞明白奈奎斯特极限定理的含义&#xff0c;网上搜了很久也没得到答案。最近深思几天后&#xff0c;终于有了点心得。顺便吐槽一下&#xff0c;csdn的提问栏目&#xff0c;有很多人用chatgpt秒回这个事&#xff0c;实在是解决不了问题&#xff0c;有时候人的问题大多数都是…

台式机安装Windows 11和Ubuntu 22双系统引导问题

一、基本情况 1.1、硬件情况 电脑有2个NVMe固态硬盘&#xff0c;1个SATA固态硬盘&#xff0c;1个机械硬盘。其中一个NVMe固态硬盘是Windows系统盘&#xff0c;另一个NVMe固态为Windows软件和文件盘&#xff0c;SATA固态硬盘为Ubuntu专用&#xff0c;机械硬盘为数据备份盘。 …

Java | Leetcode Java题解之第136题只出现一次的数字

题目&#xff1a; 题解&#xff1a; class Solution {public int singleNumber(int[] nums) {int single 0;for (int num : nums) {single ^ num;}return single;} }

Ubuntu下安装和配置Redis

目录 1、更新软件包 2、安装Redis 3、启动 Redis临时服务 4、测试Redis服务 5、配置redis服务 6、Redis服务控制命令 1、更新软件包 执行sudo apt-get update更新软件包 sudo apt-get update2、安装Redis 执行sudo apt-get install redis-server 安装命令 sudo apt i…

CLion配置

下载环境&#xff1a;MinGW-w64 - for 32 and 64 bit Windows - Browse Files at SourceForge.net 解压后找一个位置存放&#xff0c;一般放在和ide同一目录&#xff0c;方便查找 个人习惯配置调整&#xff1a; 项目创建 修改ide解码形式 项目右下角一般默认是utf8 文件编码改…

【C++题解】1074 - 小青蛙回来了

问题&#xff1a;1074 - 小青蛙回来了 类型&#xff1a;需要找规律的循环 题目描述&#xff1a; 关于小青蛙爬井的故事&#xff0c;你应该早就听过了&#xff1a;井深10 尺&#xff0c;小青蛙从井底向上爬&#xff0c;每个白天向上爬 3 尺&#xff0c;每个晚上又滑下来 2 尺&…

kafka安装流程

安装kafka前需要安装zookeeper zookeeper安装教程 1.新建一个logs文件夹 2.修改配置文件 3.修改listeners参数 4.以管理员身份启动kafka服务 .\bin\windows\kafka-server-start.bat .\config\server.properties 如果报 输入行太长。 命令语法不正确。 解决方案如下&#x…

全流程透明双语大语言模型MAP-Neo,4.5T 高质量数据训练

前言 近年来&#xff0c;大语言模型 (LLM) 已经成为人工智能领域最热门的研究方向之一&#xff0c;并在各种任务中展现出前所未有的性能。然而&#xff0c;由于商业利益的驱动&#xff0c;许多最具竞争力的模型&#xff0c;例如 GPT、Gemini 和 Claude&#xff0c;其训练细节和…