若依-框架说明书

这是一篇记录

记录各种可能碰到的操作,分为操作篇、说明篇和bug修复篇,操作篇主要记录步骤,说明篇更偏向于代码理解,bug修复篇记录下遇到的问题以及解决方案。

操作篇

字典管理

系统管理->字典管理->新增,输入信息

添加字典类型

添加成功后,点击字典类型进行详细编辑

详细编辑

点击新增,输入数据字典的信息后保存。这里应该添加多条

输入信息

保存

系统日志

系统日志

如果想要定义自己的日志记录,执行下列步骤:

数据库添加数据

sys_dict_data中,添加一行这样的数据:

1
insert into sys_dict_data values (29, 10, '立刻执行', '10', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '执行操作');
新增业务操作类型

ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java中,添加:

1
2
3
4
/**
* 立刻执行
*/
RUN,
日志标题及类型

ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java中,修改日志标题以及日志类型:

1
@Log(title = "定时任务", businessType = BusinessType.RUN)
添加注解

RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java中:

1
@Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据,10=立刻执行")

定时任务

定时任务

从一个页面传参到另一个页面

效果

我们点击学生管理,点击修改,会跳转到学生详细页面,同时将对应的数据带过去

学生管理

详细信息

如何实现

一、修改index.vue中的编辑函数:

编辑函数

this.$router.push是跳转函数,path是跳转的目的路由地址,query是要传递的参数

二、新建学生详细页面

这里可以使用若以框架自带的表单构建功能生成一个新的页面,导出为.vue文件后放入项目中的具体位置,然后在菜单管理中添加菜单,注意组件路径的填写要和实际路径保持一致

生成页面

位置

详细信息

三、修改学生详细页面的代码

假设我们新建了一个detail.vue文件,这里我们修改这三部分代码

初始化代码

第一处用于监听传来的数据,第二处是页面加载函数,第三处获取传来的参数

注意:如果传来的参数需要字典回显,还需要修改以下部分的代码:

字典代码

字典代码

至此不出意外即可成功

点击刷新表单数据

效果

表单更新

如何实现

假如我们要实现这个功能:

筛选男性

一、编写handleRefresh方法

点击

函数

  1. 打开遮罩层(即加载动画)
  2. 设置查询条件
  3. 调用查询函数
  4. 获取数据并更新
  5. 关闭遮罩层

二、后续代码实现

无弹窗跳转表格添加数据

效果

本页添加

如何实现

一、表格的每一个字段设置类似如下:

1
2
3
4
5
<el-table-column prop="contractDeliveryDate" label="产品编号">
<template slot-scope="scope">
<el-input v-model="scope.row.productNum" placeholder="请输入产品编号" />
</template>
</el-table-column>

注意将字段对应准确

二、按钮设置添加函数

1
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAddQljProduct">添加</el-button>
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
handleAddQljProduct() {
let obj = {};
obj.userInstallation = "";
obj.productNum = "";
obj.contractDeliveryDate = "";
obj.batchDeliveryDate = "";
obj.contractSignDate = "";
obj.plannedProductionPeriod = "";
obj.changeDescription = "";
obj.productModel = "";
obj.drawingNum = "";
obj.amount = "";
obj.measurementUnit = "";
obj.setType = "";
obj.executeStatus = "";
obj.productType = "";
obj.remark = "";
obj.installationMethod = "";
obj.trainContent = "";
obj.superviseRequirement = "";
obj.createUser = "";
obj.changeUser = "";
obj.changeTime = "";
obj.deleteFlag = "";
this.productList.push(obj);
},

三、序号自动增加

设置表头代码如下:

1
2
3
4
5
6
<el-table 
v-loading="loading"
:data="productList"
:row-class-name="rowQljProductIndex"
@selection-change="handleSelectionChange"
>

其中,:data是数据来源,:row-class-name是序号自增函数,设置如下:

1
2
3
rowQljProductIndex({ row, rowIndex }) {
row.id = rowIndex + 1;
},

无弹窗跳转表格删除数据

效果

无弹删除

如何实现
1
<el-button type="text" size="small" @click="handleDeleteP(scope.row)">删除</el-button>
1
2
3
4
5
/** 删除产品操作 */
handleDeleteP(row) {
const id = row.id || this.ids
this.productList.splice(id - 1, 1);
},

注意,因为我们之前添加数据的时候是将一行行数据添加进了一个列表,所以删除的时候应该将列表中的数据删除,因此这里使用了splice函数

文件上传

文件上传

表单验证

表单参数验证

自定义自增序号

效果

自增序号

如何实现

一、添加序号数组

1
2
3
4
return {
// 选中数组(序号)
nos: [],
}

二、修改handleSelectionChange函数

1
2
3
4
5
6
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.nos = selection.map(item => item.No) // 添加这一行
this.single = selection.length !== 1
this.multiple = !selection.length
},

三、添加序号函数

1
2
3
4
5
method: {
rowIndex({row, rowIndex}) {
row.No = rowIndex + 1;
},
}

四、在表单中修改相应代码

1
2
3
4
5
<el-table 
:data="productList"
:row-class-name="rowIndex"
@selection-change="handleSelectionChange"
>
1
<el-table-column prop="No" label="序号" width="50"></el-table-column>

五、对应函数中修改如下代码

1
2
3
4
5
6
7
8
9
10
handleDeleteP(row) {
const ids = row.id || this.ids;
const nos = row.No || this.nos;
this.$confirm('是否确认删除序号为"' + nos + '"的产品吗?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then
... ...
}

根据表单数据做计算

效果

求和计算

如何实现

一、定义参数

1
2
3
4
5
6
7
8
return {
// 实际到款金额
totalReceivedAmount: "",
// 累计到款比例
totalReceivedPaymentPercent: "",
// 累计欠款
totalArrears: "",
}

二、定义求和函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sum() {
getActualPayByCid(this.formData.id).then(response => {
let received_amount = 0;
let received_payment_percent = 0;
for (const p in response.data) {
received_amount += response.data[p].receivedAmount
received_payment_percent += response.data[p].receivedPaymentPercent
}
this.totalReceivedAmount = received_amount;
this.totalReceivedPaymentPercent = (Math.round(received_payment_percent * 10000)) / 100 + '%';
if (response.data.length > 0) {
this.totalArrears = response.data[0].contractAmount - received_amount;
}
});
}

思路:先使用getActualPayByCid获取表单数据(其实只是读取了数据库数据),然后对返回结果提取数据来进行处理

三、使用v-model绑定数据

1
<el-input v-model="totalReceivedAmount" placeholder="实际到款金额"></el-input>

四、在表单更新时调用求和函数

1
2
3
4
5
6
// 页面加载时
created() {
...
this.sum();
...
},
1
2
3
4
5
6
7
8
9
10
11
// 提交表单处
submitForm() {
...
addActualpay(this.actualPayInfo).then(response => {
this.msgSuccess("添加付款成功");
this.sum()
this.openActualPay = false;
this.getActualPay();
});
...
}

或许还可以在其他地方调用,看具体需求

表格数据筛选

效果

表单筛选

如何实现

一、定义筛选数据

1
2
3
4
5
6
7
8
9
10
export default {
data() {
return {
docTypeFilter: [
{text: '合同', value: 1},
{text: '技术协议', value: 2},
],
}
}
}

二、定义筛选函数

1
2
3
4
filterHandler(value, row, column) {
const property = column['property'];
return row[property] === value
}

三、表单添加筛选参数

1
2
3
4
5
6
<el-table-column
prop="docType"
label="文件类型"
:filters="docTypeFilter"
:filter-method="filterHandler">
</el-table-column>

四、设置筛选的默认值

如果你想给你的筛选器加一个初始值,添加下面这一句代码

1
2
3
4
5
6
7
<el-table-column
prop="docType"
label="文件类型"
:filters="docTypeFilter"
:filtered-value="[1]"
:filter-method="filterHandler">
</el-table-column>

存在子菜单不允许删除

效果

子菜单删除

如何实现

一、controller文件中修改代码如下

1
2
3
4
5
6
7
8
public AjaxResult remove(@PathVariable Long[] ids) {
for (Long id : ids) {
if (qljBusinessplanService.hasChildById(id)) {
return AjaxResult.error("存在子菜单,不允许删除");
}
}
return toAjax(qljBusinessplanService.deleteQljBusinessplanByIds(ids));
}

二、分别在MapperService定义对应的方法/接口

Mapper

1
2
3
4
5
6
7
/**
* 是否存在菜单子节点
*
* @param id 菜单ID
* @return 结果
*/
public int hasChildById(Long id);

Service

1
2
3
4
5
6
7
/**
* 是否存在菜单子节点
*
* @param id ID
* @return 结果 true 存在 false 不存在
*/
public boolean hasChildById(Long id);

Service(impl)

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 是否存在菜单子节点
*
* @param id ID
* @return 结果
*/
@Override
public boolean hasChildById(Long id)
{
int result = qljBusinessplanMapper.hasChildById(id);
return result > 0;
}

三、定义数据库xml代码

1
2
3
<select id="hasChildById" resultType="Integer">
select count(1) from qlj_businessplan where parent_id = #{id} and delete_flag = 0
</select>

查询数据库字段信息

效果

在数据库中我们为了更好的管理字段,会给字段加上便于理解的注释,如下:

注释

Navicat中,可以查询到这些数据,具体代码参考本文

查询

那么我们如何在代码中获得到这些字段信息呢

具体实现(基于若依)

一、定义返回类

domain目录下新建字段信息类ColumnInfo.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.ruoyi.qljsystem.domain;

public class ColumnInfo {
private String columnName; //字段名
private String columnComment; //字段说明
private String columnType; //字段类型

public String getColumnName() { return columnName; }

public void setColumnName(String columnName) { this.columnName = columnName; }

public String getColumnComment() { return columnComment; }

public void setColumnComment(String columnComment) { this.columnComment = columnComment; }

public String getColumnType() { return columnType; }

public void setColumnType(String columnType) { this.columnType = columnType; }
}

二、继续定义接口、controller、service、mapper,过程类似此处略过

三、在xml文件中定义查询函数

1
2
3
4
5
6
<select id="selectColumnInfo" resultMap="ColumnInfoResult">
select column_name, column_comment, column_type
from information_schema.COLUMNS
where table_schema = 'app_vue'
and table_name in ('qlj_product', 'qlj_contract', 'qlj_implementation_plan');
</select>

四、在这个文件中继续定义ColumnInfoResult对应的模板

1
2
3
4
5
<resultMap type="ColumnInfo" id="ColumnInfoResult">
<result property="columnName" column="column_name"/>
<result property="columnComment" column="column_comment"/>
<result property="columnType" column="column_type"/>
</resultMap>

至此,我们可以成功查询到数据,但是仍然需要做一些处理和转换。因为,注意看,我们查出来的字段信息例如change_log,不是驼峰命名,而我们代码中得到的数据是changeLog,因此,需要将下划线转换成驼峰

五、转换驼峰

1
2
3
4
5
6
7
8
9
10
11
splitOperation(stringObject, separator) {
const l = stringObject.split(separator);
for (let index = 0; index < l.length; index++) {
if (l.hasOwnProperty(index)) {
if (index !== 0) {
l[index] = l[index].replace(l[index][0], l[index][0].toUpperCase())
}
}
}
return l.join("")
}

dateRange的实现

效果

dateRange

实现

一、定义控件

1
2
3
4
5
6
7
8
9
10
11
<el-form-item label="到款日期" prop="actualDate">
<el-date-picker clearable size="small"
v-model="dateRange"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width:209px"
value-format="yyyy-MM-dd">
</el-date-picker>
</el-form-item>

其中,type="daterange"写法固定,即这条代码说明这是一个dateRange类型的日期控件,v-model="dateRange"中的dateRange需要再单独定义

1
2
3
4
5
6
data() {
return {
// 日期范围
dateRange: [],
}
}

二、调用若依自带的addDateRange方法

1
2
3
4
5
listQljProjectContractSale(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
this.qljProductList = response.rows;
this.total = response.total;
this.loading = false;
});

三、Mapper层代码

1
2
3
4
5
6
7
8
9
10
11
<select id="selectProductContractSaleList" parameterType="ProductQueryParam" resultMap="QljProductResult">
select * from qlj_salepaymentactual a
where a.delete_flag = 0
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
AND date_format(a.actual_date,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
AND date_format(a.actual_date,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
</if>
${params.dataScope}
</select>

我们注意到,参数中含有params,不是说我们就得去domain层添加params字段,因为这个参数是在BaseEntity中,具体到项目中的位置:RuoYi-Vue/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java

BaseEntity

我们只需要使用Extend继承就行,如下:

继承

至此,不出意外就可以查询成功了

说明篇

Excel导出时数据字典的值

正常情况下,导出的时候会导出数据库的值,比如

数据库

性别这一个字段会导出为0,但是当我们需要导出人类可读的信息,比如

显示

只需要在ruoyi-system下的对应模块的domain下的代码内,加上注解

1
2
3
/** 性别 */
@Excel(name = "性别", readConverterExp = "0=男,1=女,2=未知")
private Integer gender;

这样就可以导出人类可读的信息比如:性别:男

用户管理

前端页面路径

ruoyi-ui/src/views/system/user/index.vue

后端controller

ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java

数据库操作xml

ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

建议的阅读代码的顺序

顺序

按照这个顺序阅读代码可以最好的理解代码

实现当没有选中数据时按钮无法点击

效果如下:

效果

代码分析:

index.vue

代码

代码

弹窗开启/关闭

1
2
this.open = true;			# 弹窗开启
this.open = false; # 弹窗关闭

这里的open要对应

代码

新增时包含修改时不包含

效果如下:

添加用户

具体代码:

index.vue

代码

v-if:条件,如果userId未定义,说明是新增,将显示整个el-form-item;反之,证明已经存在这个userId的数据,将隐藏整个el-form-item

:expand-on-click-node

:expand-on-click-node="false"时,展开节点的同时无法触发节点事件,如下:

效果

:expand-on-click-node="true"时,展开节点的同时触发节点事件,如下:

效果

数据包分析

右键,打开浏览器的检查,切换到Network,刷新用户管理页面,选择XHR:

数据包

最下面的三个数据包名称对应于ry20210210.sql中的dict_type字段

数据库文件

然后在index.vue中获取到数据

代码

表单解读

<el-form>:代表这是一个表单
<el-form> -> ref:表单被引用时的名称,标识
<el-form> -> rules:表单验证规则
<el-form> -> model:表单数据对象
<el-form> -> label-width:表单域标签的宽度,作为 Form 直接子元素的 form-item 会继承该值
<el-form> -> <el-form-item>:表单中的每一项子元素
<el-form-item> -> label:标签文本
<el-form-item> -> prop:表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的
<el-input>:输入框
<el-input> -> v-model:绑定的表单数据对象属性
<el-input> -> style:行内样式
<el-input> -> maxlength:最大字符长度限制

回车触发聚焦@keyup.enter.native

顾名思义,按下回车键后触发的事件,给出代码示例:

前端代码:

1
2
3
4
5
6
7
8
<el-input
v-model="queryParams.roleName"
placeholder="请输入角色名称"
clearable
size="small"
style="width: 240px"
@keyup.enter.native="handleQuery"
/>

按下回车会触发handleQuery方法

1
2
3
4
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
}

分配数据权限代码解读

代码

.then()方法

then()方法只有在Promise对象中才有,异步执行,就是当.then()前的方法执行完后再执行then()内部的程序,这样就避免了,数据没获取到等的问题

res => {}

ES6中被定义为箭头函数

1
2
3
4
5
var f = v => v;
//等同于
var f = function(v) {
return v;
}
1
2
3
4
5
var f = () = 5
//等同于
var f = function() {
return 5;
}
$nextTick()

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

定义数据包字段

数据包字段的定义在对应的.xml文件中,以数据字典为例:

数据包字段

ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml

数据库文件

点击修改如何获取数据

index.vue

获取数据

->student.js

代码

->StudentController.java

代码

->IStudentService.java

代码

复制函数名,全局搜索,进入StudentMapper.xml,找到:

StudentMapper.xml

代码

在index.vue的el-dialog

代码

要和第一步保持一致!!

关闭当前选项卡

1
this.$store.dispatch('tagsView/delView', this.$route)

跳转至目标界面,指定目标界面刷新

在目标界面添加:

1
2
3
watch: {
'$route': 'getList'
},

其中'$route'对应的方法自己指定

提交功能的实现

继续以学生模块为例,具体效果如下
效果

代码实现

ruoyi-ui/src/views/student/student/detail.vue中添加代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
submitForm() {
this.$refs['elForm'].validate(valid => {
if (valid) {
updateStudent(this.formData).then(response => {
this.msgSuccess("修改成功")
// 关闭当前选项卡
this.$store.dispatch('tagsView/delView', this.$route)
// 跳转至目标页面
this.$router.push({
path: '/tool/student',
})
})
}
})
},

ruoyi-ui/src/views/student/student/index.vue中添加代码:

1
2
3
watch: {
'$route': 'getList'
}, // 目标页面刷新

动态隐藏元素

例:隐藏<el-col>标签的内容,代码如下:

1
<el-col :style="{ display: visibleParams.visible }">

el-dialog宽度调整

el-dialog增加一个custom-class,代码如下:

1
2
3
4
5
6
7
<el-dialog :title="title" :visible.sync="open" custom-class="customWidth">
...
<style>
.customWidth {
width: 60%;
}
</style>

JavaScript获取字典长度

1
let length = Object.keys(data).length

修改上传文件大小

ruoyi-admin/src/resources/application.yml中,修改如下:

upload

ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java中,修改如下:

max-size

el-input不可编辑

1
<el-input v-model="docList.contractId" placeholder="请输入合同id" :disabled="true"/>

点击table单元格触发事件

效果

table事件

如何实现
1
2
3
4
5
<el-table-column prop="urlname" label="附件名称">
<template slot-scope="scope">
<el-button @click="handleView(scope.row)" type="text">{{ scope.row.urlname }}</el-button>
</template>
</el-table-column>

注意,这里必须先使用template模板包裹起来,然后使用插槽slot-scope,同时,使用数据绑定

el-table-column隐藏列

1
<el-table-column prop="id" label="序号" v-if="show"></el-table-column>
1
2
// 是否显示列
show: true,

表单间距紧凑

1
2
3
4
5
6
7
8
<el-table v-loading="loading" :data="customerList"
:header-cell-style="{
height:'30px',
padding:'2px'
}"
:row-style="{height: '0'}"
:cell-style="{padding: '0'}"
>
1
<el-form :model="queryParams" :inline="true"label-width="80px" size="mini" >
1
2
3
4
5
6
7
8
<el-form-item label="客户姓名" prop="customerName" style="margin-bottom:5px">
<el-input
v-model="queryParams.customerName"
placeholder="请输入客户姓名"
clearable
size="small"
/>
</el-form-item>

时间搜索

你可能会发现,直接使用代码生成的检索,当时间参数存在时,检索是失败的,这是因为在检索时间的时候没有统一时间格式,正确的做法如下:

在对应的xml文件中,修改时间部分的代码:

1
2
3
4
<if test="actualReceivedDate != null ">
and date_format(actual_received_date,'%y%m%d')
=date_format(#{actualReceivedDate},'%y%m%d')
</if>

即使用date_format函数来格式化日期进而进行比较

表单字体颜色

效果

字体颜色

如何实现

一、设置风格函数

1
2
3
4
5
6
7
8
9
10
cellStyle({row, column, rowIndex, columnIndex}) {
// 状态列字体颜色
if (row.approvalStatus === '0' && columnIndex === 3) {
return 'color: #EA1B29; padding: 0;'
} else if (row.approvalStatus === '1' && columnIndex === 3) {
return 'color: #FFA500; padding: 0;'
} else if (row.approvalStatus === '2' && columnIndex === 3){
return 'color: #0CB618; padding: 0;'
}
},

二、el-table中添加参数

1
<el-table ... :cell-style="cellStyle" ...>

回到顶部

1
<el-backtop></el-backtop>

解决type=”selection”导致v-if失效的问题

只需加入这段代码即可,原理未知,很玄学

1
<el-table-column type="selection" :key="Math.random()" v-if="!edit"/>

加入:key=Math.random()即可

获取上一级路由(不是父级)

这个需求也就是获取上一个页面的路由地址,我们可以使用vue-routerbeforeRouterEnter,也就是一个路由守卫,来实现这个需求

定义路由守卫方法
1
2
3
4
5
beforeRouteEnter(to, from, next) {
next(vm => {
vm.fromPath = from.path
})
},

注意:路由守卫方法定义的位置和created方法同级

因为这里vm约等于this,因此我们需要定义上级路由参数

定义参数
1
2
// 上级路由
fromPath: "path",
同步调用

由于beforeRouteEnter中的next方法无法在页面加载完成时立刻生效,我们需要在mounted方法中定义操作

1
2
3
4
5
mounted() {
this.$nextTick(() => {
this.edit = this.fromPath !== "/develop/qljcontract"
})
},

el-input添加后缀

效果

后缀

如何实现
1
2
3
4
5
6
<el-form-item label="总价" prop="totalPrice">
<el-input v-model="formData.totalPrice" placeholder="总价" clearable>
<!-- 下面这句 -->
<i slot="suffix" style="font-style:normal;margin-right: 10px;">万元</i>
</el-input>
</el-form-item>

其中,slot="suffix"为后缀,slot="prefix"为前缀

带有建议的输入

效果

带建议的输入

如何实现

一、使用el-autocomplete组件

1
2
3
4
5
6
7
<el-autocomplete
clearable
v-model="actualPayInfo.receivedPaymentPercent"
placeholder="请输入到款比例"
:fetch-suggestions="querySearch"
:trigger-on-focus="false">
</el-autocomplete>

:fetch-suggestions是所有建议的内容

:trigger-on-focusfalse时,输入内容才会弹出建议;为true时,点击文本框就会弹出建议

二、定义到款比例参数

1
2
3
4
5
6
data() {
return {
// 到款比例
receivedPercent: [],
}
}

三、定义建议函数和过滤函数

1
2
3
4
5
6
7
8
9
10
11
querySearch(queryString, cb) {
const receivedPercent = this.receivedPercent;
const results = queryString ? receivedPercent.filter(this.createFilter(queryString)) : receivedPercent;
// 调用 callback 返回建议列表的数据
cb(results);
},
createFilter(queryString) {
return (receivedPercent) => {
return (receivedPercent.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
};
},

这两个函数只需要修改对应的参数即可,其他地方不需要做改变

四、定义loadAll()函数

1
2
3
4
5
6
7
8
loadAll() {
return [
{"value": '33'},
{"value": '60'},
{"value": '10'},
{"value": '34'}
];
},

将所有的建议内容放在这里

五、调用

1
2
3
mounted() {
this.receivedPercent = this.loadAll();
},

Excel导入根据不同字段判断重复

假如我们需要按照序号来判断导入是否重复,代码如下:

我的项目代码地址 ruoyi-system/src/main/java/com/ruoyi/qljsystem/service/impl/QljBusinessplanServiceImpl.java

判断重复

查看对应的xml代码

1
2
3
4
<select id="selectRealBusinessplanBySerialNum" parameterType="Double" resultMap="QljRealBusinessPlanResult">
<include refid="selectQljBusinessplanVo"/>
where serial_number = #{serialNumber} and delete_flag=0
</select>

正确对应附件上传地址

ruoyi-admin/src/main/resources/application.yml中,修改这个地方:

image-20210729093202728

这个地址不仅仅是附件保存的地址,还是头像储存的地方

el-input输入框事件

参考本文

el-table行点击事件

点击el-table的某一行触发的事件,代码如下

1
<el-table @row-click="testMsg" @selection-change="handleSelectionChange">

即使用@row-click

el-table点击行高亮

效果

行高亮

解决方案
1
<el-table @row-click="click" highlight-current-row>

el-table表头弹出框

效果

效果

实现

一、表格列添加render-header

1
<el-table-column label="欠款金额" prop="debtAmount" :render-header="renderHeader"/>

二、定义renderHeader方法

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
renderHeader(h, {column, $index}) {
return [
column.label,
h(
'el-tooltip',
{
props: {
placement: 'top',
effect: "light",
//content:"提示框内容",//如果提示内容短不需要换行,直接使用这个提示内容
},
},
[
h('div',
{
slot: "content",
},
[this.renderData[$index]]
),
h('span', {//问号模块
class: {
'el-icon-question': true
}
}),
],
)
]
},

bug修复篇

树表多选的异步问题

bug复现及描述

异步之前

可以看到,此场景的需求是:勾选父节点,子节点自动被选中,但是,表面上看子节点已经被选中,但是需要执行操作的时候(例如删除),却发现只能操作鼠标点击的父节点;然后,通过父节点取消选择后,发现依然可以执行删除操作,而且删除的数据正是刚刚的所有子节点数据。因此分析,一定是Dom的异步渲染造成的问题。

bug修正

一切原因出在@select参数上,原本@select参数所对应的函数如下:

1
2
3
4
5
6
7
8
9
10
// bug代码
toggleSelection(rows, flag) {
if (rows) {
rows.forEach(row => {
this.$refs.tableBusiness.toggleRowSelection(row, flag);
});
} else {
this.$refs.tableBusiness.clearSelection();
}
},

修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 无bug代码
toggleSelection(rows, flag) {
if (rows) {
this.$nextTick(() => {
rows.forEach(row => {
this.$refs.tableBusiness.toggleRowSelection(row, flag);
});
})
} else {
this.$nextTick(() => {
this.$refs.tableBusiness.clearSelection();
})
}
},

$nextTick是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用$nextTick,则可以在回调中获取更新后的DOM

修正后效果

异步之后

退出登录时总是404

bug修正

修改这个文件ruoyi-ui/src/layout/components/Navbar.vue的这个地方

href