若依(Ruoyi)单体框架使用总结 (陆续更新)
一、地址及截图
1、文档地址
2、简单截图
二、项目文件夹结构示例:
三、前端笔记
1、页面间,通过后台获取数据
//---------------------------页面-------------------------------------------
//事件
onclick='toNaviAddrPage(" + row.id + ")'
//框架自带,可修改路径
var prefix = ctx + "ve/pickup";
//方法
function toNaviAddr(id){
var url = prefix + '/toNavi/' + id;
$.modal.openTab("导航", url);
}
//获取跳转页面后台返回的数据
<script type="text/javascript" th:inline="javascript">
var dataNavi = [[${aNaviAddr}]];
console.log("data11111",dataNavi)
</script>
//---------------------------后台-------------------------------------------
@GetMapping("/toNavi/{id}")
public String toNavi(@PathVariable("id") Long id,ModelMap mmap){
//此处为假数据,可以改成处理自己的逻辑数据
Map sumList = new HashMap<>();
mmap.put("aNavi",sumList);
//前端跳转路径
return prefix + "/naviAddr";
}
2、页面间,直接传提数据(设置缓存)
//页面A传递数据
function toNaviAddr(id){
sessionStorage.setItem('pickId', id);
var url = prefix + '/toNavi/' + id;
$.modal.openTab("导航", url);
}
//页面B获取数据
function toNaviAddr(id){
const pickId = sessionStorage.getItem('pickId');
}
3、页面传递多参数
onclick='seePick(\"" + row.id + "\",\"" + row.orderId + "\")'
var url = prefix + '/seePick/' + pickUpId + "/" + orderId;
@GetMapping("/seePick/{pickUpId}/{orderId}")
public String seePick(@PathVariable Long pickUpId,@PathVariable Long orderId,ModelMap mmap){}
4、点击某格,更改颜色
//点击某格,更改颜色
function onClickCell(field, value, row, $element) {
// 获取被点击单元格的行号和列号
var rowIndex = $element.parent().data('index');
var colIndex = $element.index();
// 获取所有单元格元素
var allCells = $element.closest('table').find('td');
// 遍历所有单元格,将被点击单元格突出显示,其他单元格恢复正常
allCells.each(function() {
if ($(this).index() === colIndex && $(this).parent().data('index') === rowIndex) {
// 设置被点击单元格颜色
$(this).css('background-color', 'yellow');
} else {
// 恢复其他单元格正常颜色
$(this).css('background-color', '');
}
});
}
5、表单下拉选择另一张表的数据,例如用户选择部门
//下面shopList,通过后台查询返回
<div class="form-group" id="showMStation">
<label class="col-sm-3 control-label" >站点:</label>
<div class="col-sm-8" style="display: flex">
<select id="mStationId" name="mStationId" class="form-control">
<option th:each="shop:${shopList}" th:value="${shop.id}" th:text="${shop.name}"></option>
</select>
</div>
</div>
//js代码
function submitHandler() {
var mStationId = $('#mStationName').val();
//获取表单,添加字段
var form = $('#form-order-edit');
//定义要添加的字段
var fields = {
'mStationId':mStationId,
}
$.each(fields,function (name,value) {
var hiddenField = form.find('input[name="'+ name +'"]');
if (hiddenField.length === 0) {
// 如果不存在,就创建一个新的隐藏字段
$('<input>').attr({
type: 'hidden',
name: name,
value: value
}).appendTo(form);
} else {
// 如果已经存在,就更新它的值
if("mStationId" == name){
hiddenField.val(mStationId);
}
}
})
if ($.validate.form()) {
$.ajax({
type: "POST",
url: prefix + "后台接口",
data: $('#form-order-edit').serialize(),
success: function(response) {
if (response.code === 0) {
// 更新成功,关闭弹框
$.modal.close();
$.modal.msgSuccess(response.msg);
$.modal.reload();
} else {
// 处理错误情况
$.modal.close();
$.modal.msgSuccess(response.msg);
$.modal.reload();
}
},
error: function() {
// 处理请求失败
$.modal.close();
$.modal.msgSuccess(response.msg);
}
});
}
}
6、表单动态展示填写字段,内容可以维护
//单选表单填写,后台可以单独返回数据集合名称,进行循环,后台存json
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-1 control-label">表单名称:</label>
<div class="col-sm-8">
<div th:each="checkDetail,index : ${后台返回的数据集合名称}">
<div class="col-sm-12">
<div class="form-group">
<!-- 动态表单名称 -->
<label class="col-sm-1 control-label"><p th:text="${checkDetail.checkName}"></p></label>
<div class="col-sm-8">
<div class="radio-box" th:each="dict : ${@dict.getType('字典名称')}">
<input type="radio" th:id="${'outCheck_' + dict.dictCode}" th:name="${'outCheck_' + checkDetail.checkName}" th:value="${dict.dictValue}" th:checked="${dict.default}">
<label th:for="${'outCheck_' + dict.dictCode}" th:text="${dict.dictLabel}"></label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
//回显展示,循环的数据需放在表单对象中
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-1 control-label">表单名称:</label>
<div class="col-sm-8">
<div th:each="item, iterStat : ${循环的数据}">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-1 control-label"><p th:text="${item.key}"></p></label>
<div class="col-sm-8">
<div class="radio-box" th:each="dict : ${@dict.getType('字典名称')}">
<input type="radio" th:id="${'outCheck_' + dict.dictCode}" th:name="${'outCheck_' + item.key}" th:value="${dict.dictValue}" th:checked="${dict.dictValue==item.value}">
<label th:for="${'outCheck_' + dict.dictCode}" th:text="${dict.dictLabel}"></label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
7、上传图片
<th:block th:include="include :: bootstrap-fileinput-css"/>
<th:block th:include="include :: bootstrap-fileinput-js"/>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-1 control-label">图片:</label>
<div class="col-sm-8">
<input type="hidden" name="图片字段" th:field="*{图片字段}">
<div class="file-loading">
<input class="form-control file-upload" id="图片字段" name="files" type="file" multiple >
</div>
</div>
</div>
</div>
</div>
//添加,下面实例是上传本地
$("#图片字段").fileinput({
uploadUrl: ctx + 'common/uploads',
uploadAsync: false,
maxFileSize: 1024
}).on('filebatchuploadsuccess', function (event, data, previewId, index) {
var rsp = data.response;
$("input[name='" + event.currentTarget.id + "']").val(rsp.urls)
log.info("return urls:" + rsp.urls)
log.info("reutrn fileNames:" + rsp.fileNames)
log.info("reutrn newFileNames:" + rsp.newFileNames)
log.info("return originalFilenames:" + rsp.originalFilenames)
}).on('fileremoved', function (event, id, index) {
$("input[name='" + event.currentTarget.id + "']").val('')
})
//展示
$(".file-upload").each(function (i) {
var val = $("input[name='" + this.id + "']").val()
$(this).fileinput({
'uploadUrl': ctx + 'common/uploads',
initialPreviewAsData: true,
initialPreview: val.split(","),
uploadAsync: false,
autoReplace: true,
maxFileSize: 1024
}).on('filebatchuploadsuccess', function (event, data, previewId, index) {
var rsp = data.response;
$("input[name='" + event.currentTarget.id + "']").val(rsp.urls)
}).on('fileremoved', function (event, id, index) {
$("input[name='" + event.currentTarget.id + "']").val('')
})
$(this).fileinput('_initFileActions');
});
8、打开弹窗不显示确定按钮(修改弹窗按钮名称)
//第一种
$.modal.openOptions({
title: '全部任务',
url: "/ideabank/task/taskAll?type=2",
width: "1200",
height: "500",
showButtonPanel: false,
btn: ['关闭'],
yes: function(index, layero){
$.modal.close(index);//使用layer.close(index);无效
},
cancel: function(index, layero){
$.modal.close(index);
}
});
//第二种
layer.open({
type: 2,
title: '文档列表',
content: '/ideabank/fileInfo/listDoc/' + id,
area: ['800px', '500px'],
btn: ['关闭'],
shadeClose: true,
cancel: function(index){
layer.close(index);
}
});
//$.modal.openOptions(options) 是用于弹出窗体的自定义选项的方法。
//options 参数是一个对象,其中应该包含了配置弹窗的各种选项。
//包括以下一些常见的参数:
title(标题): 弹窗的标题。
url(链接): 弹窗中加载的内容的 URL 地址。
area(尺寸): 弹窗的尺寸,通常是一个数组,如 [‘宽度’, ‘高度’]。
btn(按钮): 弹窗底部显示的按钮,可以是一个数组,每个元素代表一个按钮的名称。
yes(确定按钮回调函数): 点击确定按钮时的回调函数。
cancel(取消按钮回调函数): 点击取消按钮时的回调函数。
showButtonPanel(按钮面板显示): 是否显示按钮面板。
maxmin(最大化最小化按钮): 是否显示最大化和最小化按钮。
shade(遮罩层): 是否显示遮罩层。
shadeClose(点击遮罩层关闭): 是否允许点击遮罩层来关闭弹窗。
//修改按钮名称
//可以在ry-ui.js中直接新增open方法,或者使用下述方法复写,
//add页面新增按钮方法对应submitHandler和saveDraftHandler方法
$.modal.openOptions({
title: '提前销售申请',
url: prefix + "/add",
width: "1100",
height: "800",
showButtonPanel: false,
btn: ['提交申请','暂存'],
yes: function (index, layero) {
var iframeWin = layero.find('iframe')[0];
iframeWin.contentWindow.submitHandler(index, layero);
},
btn2: function (index, layero) {
var iframeWin = layero.find('iframe')[0];
iframeWin.contentWindow.saveDraftHandler(index, layero); // 调用暂存处理函数
return false; // 关闭弹窗
},
end: function () {
// 在弹窗关闭时刷新当前页面
location.reload(); // 刷新页面
}
});
9、旋转/放大/缩小图片 — 列表
//列表字段
{
field: 'businessCard',
title: '营业执照',
formatter: function(value, row, index) {
if(value!='' && value != null){
var imgpath = value.split(",");
var imgHtml = '';
for(var i = 0; i < imgpath.length;i++){
imgHtml += '<img src="' + ctx + imgpath[i] + '" width="50" height="50" onclick="alertImg(\'' + ctx + imgpath[i] + '\')"/>';
}
return imgHtml;
}
else
return "--"
}
},
//方法,预览图片
function alertImg(obj) {
var url = ctx + 'common/showImg?imgUrl='+obj;
top.layer.open({
type: 2,
area: ['800px','800px'],
fix: false,
//不固定
maxmin: true,
shade: 0.3,
title: "查看图片",
content: url,
btn: ['关闭'],
// 弹层外区域关闭
shadeClose: true,
cancel: function(index) {
return true;
}
});
}
//后台转发
@GetMapping("/showImg")
public String showImg(ModelMap mmap, String imgUrl) {
mmap.put("imgUrl",imgUrl);
return prefix + "/showImg";
}
10、旋转/放大/缩小图片 — 表单
<div class="form-group">
<label class="col-sm-3 control-label">身份证:</label>
<div class="col-sm-8">
<div class="fileinput-new thumbnail img-view" >
<div th:each="imagePath : ${aTest.agentCard.split(',')}" style="display: inline-block;margin-right: 10px;">
<img onclick="alertImg(this)" style="width: 100px;height: 100px" th:src="@{/} + ${imagePath}"/>
</div>
</div>
</div>
</div>
11、表单多类目展示 — 带图片
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('测试')" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-test-edit" th:object="${test}">
<h4 class="form-header h4">信息</h4>
<input name="id" th:value="${test.id}" type="hidden">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-sm-5 control-label">名称:</label>
<div class="col-sm-7">
<input name="legalPerson" th:value="${test.person}" class="form-control" type="text" readonly>
</div>
</div>
</div>
</div>
<h4 class="form-header h4">类目1</h4>
<div class="row">
<div class="col-sm-12">
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<h4 class="form-header h4">类目2</h4>
<div class="row">
<div class="col-sm-12">
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table1"></table>
</div>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var prefix = ctx + "bi/test";
$("#form-veins-edit").validate({
focusCleanup: true
});
function submitHandler() {
if ($.validate.form()) {
$.operate.save(prefix + "/edit", $('#form-test-edit').serialize());
}
}
var elements = document.getElementsByClassName('layui-layer-btn');
// 遍历并隐藏每一个元素
for (var i = 0; i < elements.length; i++) {
elements[i].style.display = 'none';
}
$(function() {
var options = {
data: [[${test.list1}]],
pagination: false,
showSearch: false,
showRefresh: false,
showToggle: false,
showColumns: false,
sidePagination: "client",
columns: [{
checkbox: true
},
{
field: 'index',
align: 'center',
title: "序号",
formatter: function (value, row, index) {
var columnIndex = $.common.sprintf("<input type='hidden' name='index' value='%s' disabled>", $.table.serialNumber(index));
return columnIndex + $.table.serialNumber(index);
}
},
{
field: 'test1',
align: 'center',
title: '字段1',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='list1[%s].test1' value='%s' disabled>", index, value);
return html;
}
},
{
field: 'test2',
align: 'center',
title: '字段2',
formatter: function(value, row, index) {
var html = $.common.sprintf("<input class='form-control' type='text' name='list1[%s].test2' value='%s' disabled>", index, value);
return html;
}
},
]
};
$.table.init(options);
});
$(function() {
var options = {
id: 'bootstrap-table1',
data: [[${test.list2}]],
pagination: false,
showSearch: false,
showRefresh: false,
showToggle: false,
showColumns: false,
columns: [
{
field: 'index',
align: 'center',
title: "序号",
formatter: function (value, row, index) {
var columnIndex = $.common.sprintf("<input type='hidden' name='index' value='%s' disabled>", $.table.serialNumber(index));
return columnIndex + $.table.serialNumber(index);
}
},
{
field: 'test1',
align: 'center',
title: '字段1',
},
{
field: 'img1',
align: 'center',
title: '图片1',
formatter: function(value, row, index) {
if(value!='' && value != null){
var imgpath = value.split(",");
var imgHtml = '';
for(var i = 0; i < imgpath.length;i++){
imgHtml += '<img src="' + ctx + imgpath[i] + '" width="50" height="50" onclick="alertImg1(\'' + ctx + imgpath[i] + '\')"/>';
}
return imgHtml;
}
else
return "--"
}
},
]
};
$.table.init(options);
})
function alertImg1(obj) {
var url = ctx + 'common/showImg?imgUrl='+obj;
top.layer.open({
type: 2,
area: ['800px', '800px'],
fix: false,
//不固定
maxmin: true,
shade: 0.3,
title: "查看图片",
content: url,
btn: ['关闭'],
// 弹层外区域关闭
shadeClose: true,
cancel: function(index) {
return true;
}
});
}
</script>
</body>
</html>
12、提交后关闭openTab,刷新列表数据,仅供参考
//打开openTab,多参数
function toAuditStatus(id,status){
var url = prefix + '/to/' + id + '/' + status;
$.modal.openTab("审核", url, '1500', '1000');
}
//提交后关闭tab
$.operate.successTabCallback(response);
//使用示例
function submitHandler() {
if ($.validate.form()) {
// $.operate.save(prefix + "/to", $('#form-edit').serialize()); //自带
// var data = $("#form-edit").serializeArray(); //转成数组
//data.push({"name": "statusUpdate", "value": statusNew}); //添加数据
$.modal.confirm("确认要提交吗?", function() {
// 提交信息
$.ajax({
type: "POST",
url: prefix + "/to",
// contentType: "application/json",
// data: JSON.stringify(data),
data: data,
success: function(response) {
if (response.code === 0) {
// // 更新成功,关闭弹框
$.modal.msgSuccess("成功");
setTimeout(function (){
$.operate.successTabCallback(response);
},1000)
} else {
// 处理错误情况
$.modal.closeTab();
$.modal.msgSuccess(response.msg);
}
},
error: function() {
// 处理请求失败
alert("请求失败,请重试。");
}
});
})
}
}
四、后台笔记
1、获取登录者信息
User user = ShiroUtils.getSysUser();
2、集成redis
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
public RedisKeyExpirationListener(RedisMessageListenerContainer redisMessageListenerContainer) {
super(redisMessageListenerContainer);
}
/**
* 针对 redis 数据失效事件,进行数据处理
*/
@Override
public void onMessage(Message message, byte[] pattern) {
String key = message.toString();
}
}
@Configuration
public class RedisConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(); // 这里可以配置 Redis 连接信息
}
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
@Service
public class RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void setKey(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
public Object getKey(String key) {
return redisTemplate.opsForValue().get(key);
}
// 设置键值并指定过期时间
public void setKeyWithExpire(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
}
//TimeUnit.SECONDS为分钟,可以自行更改
redisService.setKeyWithExpire("","",2*60, TimeUnit.SECONDS);
2、集成AliyunOss上传
/**
* 阿里云OSS云存储
*/
public class AliyunOssUtil {
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。
//强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
private static String accessKeyId = "";
private static String accessKeySecret = "";
private static String bucketName = "anssy-general";
private static String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
//空间访问域名
private final static String bucketUrl = "";
//存储路径
public final static String uploadPrefix = "";
/**
* 文件地址上传
* @param filePath
* @param ossPath
* @throws Exception
*/
public static void uploadFile(String filePath, String ossPath) throws Exception {
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = ossPath;
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
InputStream inputStream = new FileInputStream(filePath);
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, inputStream);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
/**
* 文件流上传
* @param file
* @throws Exception
*/
public static void uploadFile(MultipartFile file, String fileName) throws Exception {
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = fileName;
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
InputStream inputStream = file.getInputStream();
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, inputStream);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
/**
* 删除文件。
* 填写需要删除的多个文件完整路径。文件完整路径中不能包含Bucket名称。
* @param keys
*/
public static void removeFile(List keys) {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(keys).withEncodingType("url"));
List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
try {
for (String obj : deletedObjects) {
String deleteObj = URLDecoder.decode(obj, "UTF-8");
System.out.println(deleteObj);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 关闭OSSClient。
ossClient.shutdown();
}
}
后续如果在使用,会继续更新
一个在学习中的开发者,勿喷,欢迎交流