开发(基于ruoyiui)笔记

问题(一)在查询时参数没有带入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
*<el-form-item label="项目赛道" prop="proTrack">*

​ *<el-select*

​ *v-model="queryParams.proTrack"*

​ *placeholder="请选择项目赛道"*

​ *clearable*

​ *@keyup.enter.native="handleQuery"*

​ *\>*

​ *<el-option v-for="item in proTrackList"*

​ *:key="item.key"*

​ *:label="item.proTrack"*

​ *:value="item.proTrack"*

​ *\>*

​ *</el-option>*

​ *</el-select>*

*</el-form-item>*

这是一段vue代码,我出现的问题是再前端页面查询的时候无法带入proTrack这个参数。

解决方法:因为我们proTrackList中没有value这个字段,导致我再绑定的时候:value=”item.value”绑定出错,将value更换为proTrack成功在查询时候带入了参数proTrack

image-20240321174607944

image-20240321174646796

还有一种没有带入参数的情况就是,有些需要参数的函数,你可能没有传入函数或者定义接受函数的变量。那这时候我们就需要去定义这个函数对于变量的传与接。下面来举一个我的错误例子

先来看我原本的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  //查询小组列表

selectGroup(){

this.loading=true;

selectGroupList(this.queryParams).then(response =>{

this.tableList = response.rows;

this.total = response.total;

this.loading=false;

})

},

//赛道改变清空组列表

selectOnChangeTrack(){

this.tableList = [];

this.selectGroup(this.Track);

},

可以看selectOnChangeTrack这个函数里,我在调用 this.selectGroup(this.Track);时,我传入了参数this.Trackthis.Track是选择框的数据,proTrack是查询参数。而我在上面的函数时,却没有接收传过来的参数this.Track ,在vue中,通常定义一个形参,在作为传来参数的载体,然后再将这个形参的值赋值给查询变量,这样就能完成参数的传递,改动如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//查询小组列表

selectGroup(tempdata){
//将形参tempdata赋值给查询参数proTrack,这样在查询时就会携带proTrack这个参数.
this.queryParams.proTrack = tempdata

this.loading=true;

selectGroupList(this.queryParams).then(response =>{

this.tableList = response.rows;

this.total = response.total;

this.loading=false;

})

},

以上即是改正后的。改正后可以带入要传的参数了。

问题(二)数据库字段名称与后端java应该对应

这是一段ruoyi对应的后端代码

1
2
3
4
5
6
7
8
9
10
11
12
<select id="selectRecommendationList" parameterType="Recommendation" resultMap="RecommendationResult">
<include refid="selectRecommendationVo"/>
<where>
<if test="time != null "> and pro_details3.time = #{time}</if>
<if test="proName != null and proName != ''"> and pro_name like concat('%', #{proName}, '%')</if>
<if test="proScore != null "> and pro_score = #{proScore}</if>
<if test="proGroup != null and proGroup != ''"> and pro_group = #{proGroup}</if>
<if test="userName != null and userName != ''"> and user_name = #{userName}</if>
<if test="deptName != null and deptName != ''"> and dept_name = #{deptName}</if>
<if test="proTrack !=null and proTrack != ''"> and pro_track=#{proTrack}</if>
</where>
</select>

mybaits中数据查询mapper文件中,值得注意的是在and后的字段名称**(比如uesr_name,dept_name,pro_track,pro_group等等)**要和数据库中的字段对应而前面的proGroup是实体类中我们自己定义的属性。

不对应,就会报错

以下这段代码是同样的道理,请注意数据字段是column后面的而实体类是property后面的,数据库字段要和自己的数据库中的字段一致(完全一致)

1
2
3
4
5
6
7
8
9
10
11
12
<resultMap type="Recommendation" id="RecommendationResult">
<result property="proId" column="pro_id" />
<result property="proName" column="pro_name" />
<result property="proScore" column="pro_score" />
<result property="proGroup" column="pro_group" />
<result property="userName" column="user_name" />
<result property="deptName" column="dept_name" />
<result property="proTrack" column="pro_track"/>
<result property="proGroupId" column="pro_group_Id"/>
<result property="proGroupName" column="pro_group_name"/>
<result property="time" column="time"/>
</resultMap>

问题(三)对vue中v-for的理解

在Vue的v-for循环中,:key:label:value是用于绑定循环中每个元素的属性的特殊语法。

  • :key用于指定循环中每个元素的唯一标识符。它是必需的,用于帮助Vue跟踪每个元素的身份,以便在更新DOM时进行高效的重用和重新排序。通常,你可以使用一个唯一的属性或索引作为key,确保在循环中的元素之间具有唯一性。
  • :label用于指定循环中每个选项的显示文本。它绑定了每个选项的显示值,这将在选择框中显示给用户。
  • :value`用于指定循环中每个选项的实际值。它绑定了每个选项的实际值,当用户选择某个选项时,该值将被传递给绑定的数据模型。

这些特殊语法(:key:label:value)是Vue中用于在循环中绑定属性的常见约定,但你也可以根据需要选择其他属性名称。重要的是确保在循环中的每个元素都有唯一的标识符作为key,以及适当的属性用于labelvalue。这样可以确保循环中的每个元素都能正确地显示和处理。

问题(四)理解一段代码的含义(以获取学院名称列表为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**获取学院名称函数 */

getDeptNameList(){

this.loading = true;

listRecommendation(this.queryParams).then(response => {

this.deptNameList = response.rows;

console.log(this.deptNameList)

this.total = response.total;

this.loading = false;

​ })

},

下面对这段代码剖析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
getDeptNameList() {  this.loading = true; *// 设置 loading 状态为 true,表示正在加载数据*  

*// 调用 listRecommendation 函数,并传递 this.queryParams 参数*

listRecommendation(this.queryParams).then(response => {
this.deptNameList = response.rows; *// 将返回的学院名称列表赋值给 this.deptNameList*

console.log(this.deptNameList); *// 打印学院名称列表到控制台*

this.total = response.total; *// 设置总数为返回结果的总数*

this.loading = false; *// 设置 loading 状态为 false,表示加载数据完成*

}); },

帮助理解

思路(一)选择框一限定选择框二的内容

如果想实现两个选择框第一个选择框里面的所选择的内容可以改变第二个选择框里面的数据(比如选择了第一个框里面的2021年,那第二个选择框就会显示在2021里面有什么内容)

可以考虑使用@change=””来绑定一个变化,只要第一个选择框里面的内容变了,那么就会执行@change绑定的事件比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 //选择框一
<el-form-item label="选择赛事:" prop="time">

​ <el-select

​ v-model="queryParams.time"

​ placeholder="请选择参赛时间"

​ clearable //表示是否可以清除选择框内容

​ @change="handleSelectionChange1"

​ \>

​ <el-option v-for="dict in dict.type.events_year"

​ :key="dict.key"

​ :label="dict.value"

​ :value="dict.value">

​ </el-option>

​ </el-select>

</el-form-item>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//选择框二
el-form-item label="" prop="selectCompetition">

​ <el-select

​ class="custom-select"

​ v-model="queryParams.selectCompetition"

​ placeholder="请选择赛事"

​ clearable

​ @keyup.enter.native="handleQuery" //回车

​ \>

​ <el-option v-for=" item in competitionList"

​ :key="item.key"

​ :label="item.eventName"

​ :value="item.value">

​ </el-option>

</el-select>

</el-form-item>
1
2
3
4
5
6
7
8
9
10
11
12
//@change的所绑定事件的功能
handleSelectionChange1() {

this.queryParams.startTime = this.queryParams.time; // Update the query parameter

this.queryParams.selectCompetition = null; // Clear the competition dropdown

this.competitionList = []; // Clear the competition list

this.getEventsList(); // Call the function to fetch the updated competition list

},

问题(五):在今天编写代码时候遇到了这么一个问题,就是关于添加数据前端页面他不给我显示。

image-20240327175415722

查看发现是getfilename函数作怪,因为我的这个函数是要获取路径去分割,然而我并没有在数据库给出路径,所以这两个数据显示不出来,更改函数后成功显示,函数更改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
  getfileName(path){

if(path){

return path.split('/').pop();

}else{

return "暂无资料";

}

},

方法1:在数据库中呢,我删除一个数据,由于这个数据关联很多表,那就需要一同删除,这可以利用触发器,以下是我在该项目中写道的触发器

1
2
3
BEGIN
DELETE FROM pro_coreteam WHERE pro_id = OLD.pro_id;
END

该触发器是当我们根据项目id(pro_id)删除数据时,他会带着通过项目id关联的id里面的内容一同删除,删除的表名称、是pro_coreteam

4.2新添加了一个触发器代码如下

1
2
3
4
5
6
7
8
9
10
11
DELIMITER //
CREATE TRIGGER update_group_id
AFTER INSERT ON pro_mapping_group
FOR EACH ROW
BEGIN
UPDATE pro_details3
SET pro_group_id = NEW.group_id
WHERE pro_id = NEW.pro_id;
END;
//
DELIMITER ;

解释

在这个触发器中,当在 pro_mapping_group 表中插入新的记录时,将会更新 pro_details3 表中对应 pro_idpro_group_id 字段。这里假设 pro_mapping_group 表和 pro_details3 表通过 pro_id 字段关联。如果你的实际情况不同,请根据实际情况修改 WHERE 子句。

请注意,这个触发器只在插入新的记录时触发,如果你更新 pro_mapping_group 表中的 group_id 字段,这个触发器不会触发。如果你希望在更新 group_id 时也触发,你需要创建一个额外的 AFTER UPDATE 触发器

方法2:如何写一个对话框即点击按钮弹出对话框?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<!-- ... -->
<el-button type="success" @click="showDialog">导入互联网+大赛官网(大创网)项目资料</el-button>
<el-dialog title="导入互联网+大赛官网(大创网)项目资料" :visible.sync="dialogVisible" width="800px" top="5vh" append-to-body>
<!-- 对话框内容 -->
<!-- ... -->
</el-dialog>
</template>

<script>
export default {
// ...
data() {
return {
dialogVisible: false, // 对话框可见性
};
},
methods: {
showDialog() {
this.dialogVisible = true; // 点击按钮时显示对话框
},
// 其他方法
// ...
},
};
</script>

方法3:如何在插入数据时携带其他参数

新建你要插入的属性比如我下面的

1
2
3
4
5
@Excel(name = "学生账号")
private Long StuNumber;

/** 学生id*/
private Long userId;

在服务层写好定义的方法

1
public int insertCore(ProCoreteam proCoreteam);

实现类中实现方法

1
2
3
4
5
@Override
public int insertCore(ProCoreteam proCoreteam)
{
return proManagerMapper.insertCore(proCoreteam);
}

mapper中定义方法

1
public int insertCore(ProCoreteam proCoreteam);

xml文件中使用方法

1
2
3
4
<insert id="insertCore" parameterType="ProCoreteam"  >
insert into pro_coreteam
(pro_id,student_id) value (#{proId},#{studentId})
</insert>

同理按照以上步骤创建stuNumber的方法

1
2
3
<select id="selectstuIdBystuNumber" parameterType="long" resultMap="ProManagerResult">
select user_id from sys_user where phonenumber = #{stuNumber}
</select>

关键点在下面,我们要先获取插入完成以后的proid

1
2
3
4
5
6
7
--      
<insert>
//获取插入完以后的proid
<selectKey resultType="Long" keyProperty="proId" order="AFTER">
select LAST_INSERT_ID()
</selectKey>
</insert>
  • 1.最外层的<insert></insert>没有返回属性(resultType),但是里面的<selectKey></selectKey>是有返回值类型的。
  • 2.order="AFTER"表示先执行插入,之后才执行selectkey语句的。
  • 3.select @@identityselect LAST_INSERT_ID()都表示选出刚刚插入的最后一条数据的id。
  • 4.实体类中id属性字段一定需要set以及get方法
  • 5.此时,接口中仍不需要有返回值,框架会自动将值注入到我们insert的那个对象中,我们可以直接使用就可以了。

其实,我们的接口中可以有返回值,但是这个返回值不是id,而是表示插入后影响的行数,此时sql中仍和上面一样,不需要写返回值。

1
2
3
4
5
6
7
<insert id="insertStudentCacheId" parameterType="Student">
insert into student(name,age,score) values(#{name},#{age},#{score})
<!-- 指定结果类型resultType,keyProperty是属性,自动返回到属性id中,order是次序,after是指获取id是在于插入后 -->
<selectKey resultType="int" keyProperty="id" order="AFTER">
select @@identity
</selectKey>
</insert>

最最最核心的一步

1
2
3
4
5
6
7
8
//执行完以后主键自增,获取到自增的proId所以要先执行
proManagerMapper.insertProManager(proManager);
//创建实体类ProManager的对象S存储通过学生账号获得stuid
ProManager S = proManagerMapper.selectstuIdBystuNumber(proManager.getStuNumber());
//带参数的构建对象方法,上面已经获取到了proId和studentId所以下面创建ProCoreteam的对象p_core存储这两个id
ProCoreteam p_core=new ProCoreteam(proManager.getProId(),S.getUserId());
//调用插入方法插入id,由于是个导入过程所以会在导入过程中执行以上内容自动插入,这样就携带了其他参数
proManagerMapper.insertCore(p_core);

问题(六)基于ruoyiUI新建对话框的一些属性问题

我们首先新建一个对话框,代码如下(我这个时ruoyiUI的对话框建法)

1
<el-dialog  :title="title"    :visible.sync="open" width="1500px" append-to-body> </el-dialog>
  • :title="title":此属性设置对话框的标题。title 应该是你的 Vue 实例的 data 属性之一,用来存放对话框的标题。: 前缀表示这是一个动态属性,它的值会被解析为 JavaScript 表达式。
  • :visible.sync="open":此属性控制对话框的可见性。如果 opentrue,对话框就会显示;如果 openfalse,对话框就会隐藏。.sync 修饰符表示这个属性是双向绑定的:当对话框的可见性改变时,open 的值也会相应地改变(对话框自带的×)。
  • width="1500px":此属性设置对话框的宽度。在这个例子中,对话框的宽度被设置为 1500 像素。
  • append-to-body:此属性表示对话框将被附加到 body 元素。这可以防止对话框被其他 CSS 影响,确保它能正确地显示在其他元素之上。这是一个布尔属性,如果存在,其值就是 true

方法4: 如何获取当前行id

面对这个问题我只想说,这可真是让我想的酣畅淋漓的一次啊,为什么这么说?先来看看我犯了什么错误把
首先,我当时一心只想着获取当前行id,但是却忽视了原表格是一个静态表格这个前提。所以我花了快一个小时,唉还好脑子转得快(快什么,要是快的话早想到了)突然意识到了,这是一个静态表格,我去哪里动态获取当前行?

其次,因为脑子当时很热,一点思路也没有,所以就乱撞,白白荒废了时间。

接下来,让我来整理获取当前行id的一个思路

首先想,你要在哪一行操作,好的定位到这一行,要用<template></template>标签,具体写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<el-table-column prop="selectPro" label="查看项目" width="208">

<template slot-scope="scope">

<el-button

class="ebtn1"

style="color: white;"

@click="getAllocationList(scope.row.groupId)"

\>查看</el-button>

</template>

</el-table-column>

比如我就是这一行要注意了,绑定点击事件的时候,必须加scope.row.xxxx其中的xxxx是你要获取的什么什么id,那么这样你就可以获取到当前行的id了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
getAllocationList(groupId){

console.log(groupId)

this.loading = true;

alreadyAllocation(groupId).then(response =>{

this.alAllocationList = response.rows;

this.total = response.total;
this.loading=false;

})

}

可以在你所用的时间console.log(groupId)你获取的id然后查看控制台有没有对应的id输出,然后传递参数就可以了。

你甚至还可以这么写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
getAllocationList(groupId){
// 输出传入的 groupId
console.log(groupId)
// 创建一个新对象,包含 groupId
const params = {groupId}
this.loading = true;
//传递参数
alreadyAllocation(params).then(response =>{

this.alAllocationList = response.rows;

this.total = response.total;
this.loading=false;

})

}

也是可以的,ok今天就写这么多0.0

问题7:在传入参数时的错误

报错是这样对的

Error: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'groupId' not found. Available parameters are [arg1, arg0, param1, param2] 翻译一下就是

错误:嵌套异常是 org.apache.ibatis.binding.BindingException:找不到参数“groupId”。可用参数为 [arg1, arg0, param1, param2]

这是我传入参数的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
getAllocationList(row){

const proTrack = this.Track;

const groupId=row.groupId;

const groupName=row.groupName;

this.loading = true;

alreadyAllocation(groupId,proTrack).then(response =>{

this.alAllocationList = response.data;

// console.log(this.alAllocationList)

this.sendArrayToParent(this.alAllocationList);

this.sendArrayToParent2(groupName);

// console.log(tableList);

this.total = response.total;

this.loading=false;

})
}

这是js里面对应的接口

1
2
3
4
5
6
7
8
9
10
11
export function alreadyAllocation(groupId,proTrack){

return request({

url: '/project/csxmfp/group/' + groupId +'/'+ proTrack,

method: 'get'

})

}

报错问题是gruopId找不到,可用参数给出了[arg1, arg0, param1, param2] 这四个参数,这时我就在想,那是不是传入的参数得用这四个中的几个呢?于是我检查了后端,在处理器(proAllocationController)中打印了我从前端带过来的两个参数

1
2
3
4
5
6
7
8
9
10
11
12
    /**
* 通过赛道名称和小组获取参赛项目分配详细信息
*/
@PreAuthorize("@ss.hasPermi('project:csxmfp:querybygroupId')")
@GetMapping(value = "group/{groupId}/{trackName}")
public AjaxResult getInfoByGroupId(@PathVariable("groupId") Long groupId,@PathVariable("trackName") String trackName)
{
System.out.println(groupId);
System.out.println("aaaa"+trackName);
return success(proAllocationService.selectGroupProjectByGroupId(groupId,trackName));
}
}

控制台输出

image-20240403220611894

可以看到控制台是有groupId的输出1这就是说拿到了前端数据那为什么后端确说我没有定义呢?检查xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="selectGroupProjectByGroupId" parameterType="GroupProject" resultMap="GroupProjectresultMap">
SELECT
pro_details3.pro_id,
pro_details3.pro_name,
pro_details3.pro_logo,
pro_details3.pro_track,
pro_details3.pro_group_id
FROM
pro_details3
INNER JOIN
pro_mapping_group ON pro_details3.pro_id = pro_mapping_group.pro_id
<where>
pro_mapping_group.group_id = #{group_id}

<if test="proTrack != 'null' and proTrack != '' and arg1!= null"> and pro_details3.pro_track = #{proTrack}</if>
</where>
ORDER BY
pro_details3.pro_id;
</select>

于是就想到会不会是参数的传递问题呢?于是我就尝试用arg0代替groupId,用age1代替了proTrack,下面是更新完xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="selectGroupProjectByGroupId" parameterType="GroupProject" resultMap="GroupProjectresultMap">
SELECT
pro_details3.pro_id,
pro_details3.pro_name,
pro_details3.pro_logo,
pro_details3.pro_track,
pro_details3.pro_group_id
FROM
pro_details3
INNER JOIN
pro_mapping_group ON pro_details3.pro_id = pro_mapping_group.pro_id
<where>
pro_mapping_group.group_id = #{arg0}

<if test="arg1 != 'null' and arg1 != '' and arg1!= null"> and pro_details3.pro_track = #{arg1}</if>
</where>
ORDER BY
pro_details3.pro_id;
</select>

运行代码,成功传递了数据,问题解决了。你要是问我为什么这样做,其实我也不清楚,但是通过报错信息给出了可用的四个参数,所以说有错误还是要从报错信息入手啊

问题8:vue+ruoyi前端内网在线预览ppt

在内网在线预览ppt时,我踩了很多坑,这里记录一下我都翻过什么错误。

  • 错误一:在给数据模型里面的数据赋值时,没有搞清楚层级,来看代码image-20240506225835352这里,我定义了一个prevForm对象,里面有prevurl两个数据。然而在我进行赋值操作时错写成了this.prev = xxxx,this.url=xxxx,看似没错,但是实际上已经少写了一层了那就是丢了一个最大的对象prevForm正确写法是这样的image-20240506230117170

  • 错误二:image-20240506230225944没有搞清楚=,==,===这三个符号的意思,=:意味着赋值操作,左边的值赋值给右边的,==是比较运算符,当左右两边相等时返回true,不相等时返回false,而===在前端中表示全等,意味着完全相等,意思就是在做运算判断时,等式两边的数值以及数据类型都必须相同。

  • 在线预览ppt:

  • 思路是,我在a标签中写了一个函数whatType用来判断这是一个以什么结尾的文件,这里用到了方法pop()取最后一个元素

    • image-20240506230845450
  • 来说一下在线预览ppt的模板:

    • 第一步,确定你要在线预览文件的类型:

      1
      2
      3
      4
      5
      6
      7
      8
      //docx文档预览组件
      npm install @vue-office/docx vue-demi

      //excel文档预览组件
      npm install @vue-office/excel vue-demi

      //pdf文档预览组件
      npm install @vue-office/pdf vue-demi
    • vue模板

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      <template>
      <vue-office-docx :src="docx" @rendered="rendered"/>
      <vue-office-excel :src="excel" @rendered="rendered"/>
      <vue-office-pdf :src="pdf" @rendered="rendered"/>
      </template>

      <script>
      //引入VueOfficeDocx组件
      import VueOfficeDocx from '@vue-office/docx'
      //引入VueOfficeExcel组件
      import VueOfficeExcel from '@vue-office/excel'
      //引入VueOfficePdf组件
      import VueOfficePdf from '@vue-office/pdf'
      //引入相关样式
      import '@vue-office/docx/lib/index.css'

      export default {
      components:{
      VueOfficeDocx
      },
      data(){
      return {
      docx: 'http://static.shanhuxueyuan.com/test6.docx' //设置文档网络地址,可以是相对地址
      excel:
      pdf:
      }
      },
      methods:{
      rendered(){
      console.log("渲染完成")
      }
      }
      }
      </script>

一次性引入了三个,这样会渲染三个

  • 最后,展示一下我写的点击弹出弹窗,来展示对应的内容,代码如下(只展示核心部分,多余代码省略):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    <template>
    .........
    <el-table-column label="项目计划书" align="center" prop="proPlan" >
    <template slot-scope="scope">
    <a @click="whatType(scope.row.proPlan)" v-if="getfileName(scope.row.proPt) !== '暂无资料'">
    {{ getfileName(scope.row.proPlan) }}
    </a>
    <a v-else>
    {{ getfileName(scope.row.proPlan) }}
    </a>
    </template>
    .......
    <!--弹出各种文件预览弹窗-->
    <el-dialog :visible.sync="dialogPrevVisible" title="文件预览" width="90%" >
    <el-form style="margin-top: 10px;" :model="prevForm">
    <template v-if="prevForm.prev == 'docx'">
    <vue-office-docx style="height: 90%" :src="prevForm.url" @rendered="rendered"/>
    </template>
    <template v-if="prevForm.prev == 'xlsx'">
    <vue-office-excel style="height: 90%" :src="prevForm.url" @rendered="rendered" />
    </template>
    <template v-if="prevForm.prev == 'pdf'">
    <vue-office-pdf style="height: 90%" :src="prevForm.url" @rendered="rendered"/>
    </template>
    </el-form>
    </el-dialog>
    ......
    </template>
    <script>
    //引入VueOfficeDocx组件
    import VueOfficeDocx from '@vue-office/docx';
    import VueOfficeExcel from '@vue-office/excel';
    import VueOfficePdf from '@vue-office/pdf';
    //引入相关样式
    import '@vue-office/docx/lib/index.css';
    data() {
    return {
    //设置弹窗初始状态
    dialogPrevVisible:false,
    prevForm:{
    prev:'',
    url:''
    },
    .....
    }
    methods: {
    //判断路径的类型
    whatType(path){
    //获取文件拓展名并改为小写
    var tempPath = path;
    console.log(tempPath);
    //pop函数取最后一个元素,然后在转成小写。
    var fileType = path.split('.').pop().toLowerCase();
    console.log(fileType);

    if(fileType == 'docx'){
    this.prevForm.prev = 'docx',
    this.prevForm.url='http://192.168.1.104/dev-api'+ tempPath
    this.dialogPrevVisible = true;

    }else if(fileType == 'excel'){
    this.prevForm.prev = 'excel',
    this.prevForm.url='http://192.168.1.104/dev-api'+ tempPath
    this.dialogPrevVisible = true;

    }else if(fileType =='pdf'){
    this.prevForm.prev = 'pdf',
    this.prevForm.url='http://192.168.1.104/dev-api'+ tempPath
    this.dialogPrevVisible = true;
    }else{
    window.alert("不支持查看该文件格式")
    }
    },
    rendered(){
    console.log("渲染完成")
    },
    }
    </script>

Vue中v-model数据绑定问题(绑定对象)

  • 在vue中,选择框中如果需要传递多个值到后端进行带参数的查询,这时,我们就应该将这两个值,绑定在一个对象上,那么关于如何绑定到对象以及进行参数的传递,下面是我的理解:

  • 示例代码:

    • <el-form :model="queryParams">
                <el-form-item label="请选择赛事:" prop="proGameId">
                  <el-select v-model="this.selectedOption" placeholder="请选择下拉选择" clearable :style="{width: '100%'}" @change="getList2">
                <el-option v-for="(item, index) in EventList" :key="index"  :label="`${item.proGame}——${item.groupName}`"
                :value="{ proGameId: item.proGameId, groupId: item.groupId }" :disabled="item.disabled"></el-option>
              </el-select>
                </el-form-item>
              </el-form> 
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57

      解释:**:label="`${item.proGame}——${item.groupName}`"**绑定成这样的label,在我们的选择框中就会是下面的样式

      ![image-20240610141757357](../images/开发-基于ruoyiui-笔记/image-20240610141757357.png)

      **:value="{ proGameId: item.proGameId, groupId: item.groupId }"**通过这样的绑定,想要将proGameId和groupId同时绑定到该选择框中,那么首先v-model的绑定应该是一个对象![image-20240610141930131](../images/开发-基于ruoyiui-笔记/image-20240610141930131.png)

      并且在对象中,还必须包含你所绑定的这两个属性值,初始化为null。(初始化为空的原因是对象初始化就是空对象),如下图

      ![image-20240610142031387](../images/开发-基于ruoyiui-笔记/image-20240610142031387.png)

      这样,就完成了数据在选择框层面的绑定。那么如何传递这两个数据呢?比如我今天就遇到这样的问题,从后端查询到这两个的值,同时也需要把查询到的值 ,再绑定到选择框中,当选择框的内的选项一改变就会传递这两个对象的值到后端再进行一次查询。

      首先要做的就是绑定@change事件,比如我上面代码中的

      ![image-20240610142336415](../images/开发-基于ruoyiui-笔记/image-20240610142336415.png)

      然后

      ![image-20240610142457202](../images/开发-基于ruoyiui-笔记/image-20240610142457202.png)

      这样就成功把值带到查询参数里面,就可以在次传递给后端进行查询啦。

      最后,如果在调用函数getList2的时候我并没有指定参数,那么在方法中的getList2(value)中,这个value就指的是:value所绑定的内容。也就是用户选择的值

      # 文件的上传

      ## 二进制blob传输

      Blob(Binary Large Object)是一种用于存储大量二进制数据的数据类型。在数据库中,BLOB 通常用来存储图像、文档、视频等文件。而在Web开发中,Blob对象表示一个不可变的、原始数据的类文件对象。Blob 表示的数据不一定是一个JavaScript原生格式。Blob 对象可以表示一个文本文件或一个图片文件等。

      ### Blob的基本概念

      - **Blob对象**:Blob对象表示一个不可变的、原始数据的类文件对象。Blob表示的数据不一定是一个JavaScript原生格式。Blob对象可以表示一个文本文件或一个图片文件等。

      - **File对象**:File对象是Blob对象的一个子类,主要用于表示用户从设备中选择的文件。每个File对象都有一个name属性(文件名)和一个lastModified属性(文件最后修改的时间戳)。

      - 一个file对象通常利用事件对象获取

      - **FileReader对象**:FileReader接口提供了一个API,允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用FileReader对象可以读取Blob或File对象中的数据。

      - 值得注意的是,该对象中包含了下面的内容

      ![image-20240919184056798](../images/开发-基于ruoyiui-笔记/image-20240919184056798.png)、

      包括读取完毕的result和一些回调,所以读取是异步任务


      ### 创建Blob对象

      可以通过多种方式创建Blob对象,以下是一些常见的方法:

      - **从字符串创建**:

      ```javascript
      const text = "Hello, world!";
      const blob = new Blob([text], {type: "text/plain"});
  • 从ArrayBuffer创建

    1
    2
    const buffer = new ArrayBuffer(8);
    const blob = new Blob([buffer], {type: "application/octet-stream"});
  • 从文件输入创建

    1
    <input type="file" id="fileInput">
    1
    2
    3
    4
    document.getElementById('fileInput').addEventListener('change', function(e) {
    const file = e.target.files[0];
    // file 就是一个Blob对象
    });

使用Blob对象

  • 转换为URL可以用于前端页面的img标签的url来直接显示图片

    1
    2
    3
    4
    5
    const blobUrl = URL.createObjectURL(blob);
    // 使用blobUrl作为img元素的src属性
    const img = document.createElement('img');
    img.src = blobUrl;
    document.body.appendChild(img);
  • 发送到服务器利用FormData类来向服务器发送。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const formData = new FormData();
    formData.append('file', blob, 'filename.txt');
    //fetch返回了一个Promise对象。
    fetch('/upload', {
    method: 'POST',
    body: formData
    })
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));
  • 读取Blob内容利用FileReader来异步读取对象。注意是等待读取完之后再输出内容。

    1
    2
    3
    4
    5
    6
    7
    const reader = new FileReader();
    reader.onload = function(e) {
    // const content = reader.result //这样写也可以
    const content = e.target.result; // Blob的内容
    console.log(content);
    };
    reader.readAsText(blob); // 或者使用readAsDataURL, readAsBinaryString, readAsArrayBuffer 这里读取完成后会触发onload事件,随之输出内容
  • **fr.readAsText(blobFiles)**:以文本方式读取

  • **fr.readAsBinaryString(blobFiles)**:以二进制字符串方式读取

  • **fr.readAsDataURL(blobFiles)**:以base64方式读取文件

}

注意事项

  • 内存管理:使用完Blob URL后,应该调用URL.revokeObjectURL(blobUrl)来释放与该URL关联的内存。
  • 类型安全:当创建Blob时指定正确的MIME类型,有助于确保数据以正确的方式被处理。
  • 浏览器兼容性:虽然大多数现代浏览器都支持Blob,但在使用时仍需检查目标浏览器的支持情况。

文件的下载

使用a标签的download属性

这是最简单直接的方法,适用于浏览器支持 download 属性的情况。通过设置 <a> 标签的 href 属性为文件的 URL,并添加 download 属性来指定下载的文件名。

1
<a href="path/to/your/file.pdf" download="newFileName.pdf">点击下载</a>

使用第三方库来进行下载

如果您希望在Web应用中通过第三方库实现文件下载功能,这里有几个常用的JavaScript库可以帮助您简化这一过程。这些库通常提供了更丰富的功能,如进度条显示、断点续传等。下面是一些流行的库及其基本用法:

1. axios

axios 是一个基于Promise的HTTP客户端,可用于浏览器和node.js。它支持跨域请求,并且可以方便地发送GET/POST请求。

安装:

1
npm install axios

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import axios from 'axios';

axios({
url: 'http://127.0.0.1:8081/download',
method: 'GET',
responseType: 'blob', // 重要:设置响应类型为 blob
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'filename.ext'); // 设置下载的文件名
document.body.appendChild(link);
link.click();
});

2. file-saver

file-saver 是一个用于浏览器的库,可以非常方便地保存文件到用户的计算机上。

安装:

1
npm install file-saver

使用示例:

1
2
3
4
5
6
7
8
9
import { saveAs } from 'file-saver';

axios({
url: 'http://127.0.0.1:8081/download',
method: 'GET',
responseType: 'blob',
}).then((response) => {
saveAs(response.data, 'filename.ext'); // 直接使用 file-saver 库保存文件
});

3. js-file-download

这是一个轻量级的库,专门用于将Blob对象转换为文件并触发下载。

安装:

1
npm install js-file-download

使用示例:

1
2
3
4
5
6
import download from 'js-file-download';

axios.get('http://127.0.0.1:8081/download', { responseType: 'blob' })
.then((res) => {
download(res.data, 'filename.ext');
});

注意事项

  • CORS 配置:确保您的服务器已经正确配置了CORS,允许来自您的前端应用的请求。参考我之前提供的关于如何配置CORS的信息。
  • 安全性:在生产环境中,确保下载链接的安全性,避免暴露敏感数据。
  • 文件大小限制:对于大文件下载,考虑使用流式传输或分块下载以提高性能和用户体验。

以上就是使用第三方库在Web应用中实现文件下载的一些方法。选择合适的库可以根据您的具体需求来决定。

基本过程

image-20241002192615353

代码中的数学

动态渲染代码文本中的数学表达式

这里我推荐使用MathJax3.x版本。

使用前须知:渲染可以选择异步和同步渲染,具体看需求,但切记,渲染是在数据加载完毕之后进行的,如果过早的引入或使用会导致数据还未被渲染就呈现在页面上了。

1.随便目录下新建(我这里使用的是vue-ruoyi框架),在utils下新建MathJax.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 设置 MathJax 配置
window.MathJax = {
tex: {
inlineMath: [['$', '$'], ['\$', '\$']],
displayMath: [['$$', '$$'], ['\\$', '\\$']], //这里的转义字符
},
chtml: {
scale: 1.0
}
};

// 创建 script 元素用于加载 MathJax
(function () {
let script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js';
script.defer = true;

// 监听 script 加载完成事件
script.onload = function() {
// MathJax 加载完成后执行的代码
console.log('MathJax加载成功,可以使用.');

// 在这里安全地调用 MathJax.typesetPromise
if (typeof MathJax !== 'undefined' && typeof MathJax.typesetPromise === 'function') {
MathJax.typesetPromise().then(() => {
console.log('MathJax 准备完毕.');
}).catch(err => {
console.error('发生错误,类型设置失败:', err);
});
} else {
console.error('MathJax 或 typesetPromise 不可用.');
}
};

// 如果加载失败则输出错误信息
script.onerror = function() {
console.error('MathJax加载失败.');
};

// 将 script 添加到文档头部
document.head.appendChild(script);
})();

2.vue中可以先引入,因为我这里需要的是异步渲染所以我直接在mouted钩子中进行加载

image-20241216170408765

控制台
image-20241216170620784

然后通过vue的钩子$nextTick在需要等待dom加载完毕的地方调用即可。

MathJax3.x 中的api

MathJax.typeset()同步方法

MathJax.typesetPromise()异步方法

可以选择封装一个渲染函数来动态的使用

1
2
3
4
5
6
7
8
9
10
11
//渲染数学公式
renderMath(){
this.$nextTick(function () {
MathJax.typesetPromise()
.then(res => {
console.log("渲染已经完成")
}).catch(()=>{
console.log("渲染失败")
})
})
},

好了,可以完整的在页面上显示数学公式了。如果你还想更加仔细的配置MathJax请参考官方文档。