Merge branch 'feature/develop1.0'
This commit is contained in:
commit
0779f311e0
20
README.md
20
README.md
|
@ -52,6 +52,10 @@
|
|||
<td><img src="https://qimetons.oss-cn-beijing.aliyuncs.com/de48282f792d4300ba26b7a397464d71.png"/></td>
|
||||
<td><img src="https://qimetons.oss-cn-beijing.aliyuncs.com/952eaddf26164feaae13ec9ffdbeab35.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://qimetons.oss-cn-beijing.aliyuncs.com/593f9a39070b4a5f879b91d94c6cc65e.png"/></td>
|
||||
<td><img src="https://qimetons.oss-cn-beijing.aliyuncs.com/75db4a86e2224be7bdfd3de6e6cce43b.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://qimetons.oss-cn-beijing.aliyuncs.com/416ffa7c1f1f42f8807a24dc2e63a08d.png"/></td>
|
||||
<td><img src="https://qimetons.oss-cn-beijing.aliyuncs.com/0ffee016a6be4d5eb5b5f18a0291353e.png"/></td>
|
||||
|
@ -64,7 +68,7 @@ v1.0 2021-2-24 ding-flow首发
|
|||
- 2.0 抛弃官方设计器用户组,使用ding-flow自带用户,流程角色。流程角色实现向下兼容(即流程某节点配置父角色,其父角色下所有子角色都具有流程审批权限)
|
||||
- 3.0 系统自带请假申请和采购单申请,采用类form表单提交,保存流程资源表。反序列化表单,实现流程中取业务数据。接口通用,一个接口按照规定格式设计流程即可。
|
||||
- 4.0 全局监听器抽象设计,实现任务创建时,钉钉消息通知,待办人实时接收消息,及时处理任务。
|
||||
- 5.0 任务监听器抽象设计,满足各种节点自定义业务需求,按照各种开发即可。
|
||||
- 5.0 任务监听器抽象设计,满足各种节点自定义业务需求,按照规则开发即可。
|
||||
- 6.0 系统创建用户和部门,同步到钉钉用户和部门。实现钉钉移动化办公。
|
||||
- 7.0 钉钉修改用户和部门,系统异步监听数据变化,实时修改系统数据。实现钉钉和系统数据同步。
|
||||
- 8.0 账单管理,导出支付宝,微信账单。一键导入系统,系统轻松管理账单数据,日常消费明了清晰。
|
||||
|
@ -76,15 +80,15 @@ v1.1 2021-2-26
|
|||
|
||||
## 未来规划
|
||||
|
||||
- 1.0 角色与钉钉打通
|
||||
- 2.0 流程表单设计
|
||||
- 3.0 钉钉工作流与系统工作流打通(让流程移动化)
|
||||
- 4.0 钉钉同步数据失败后,手工重试
|
||||
- 5.0 记录消息通知轨迹
|
||||
- 6.0 钉钉流程和系统流程打通,让流程移动化
|
||||
- 1.0 流程表单设计
|
||||
- 2.0 钉钉工作流与系统工作流打通,让流程移动化
|
||||
- 3.0 钉钉同步数据失败后,手工重试
|
||||
- 4.0 记录消息通知轨迹
|
||||
- 5.0 钉钉机器人集成
|
||||
|
||||
## 交流群
|
||||
- QQ群: 577813338 欢迎入群讨论,我们的口号:借助钉钉,致力于流程,让流程更简单
|
||||
- QQ群: 577813338
|
||||
- 欢迎入群讨论,我们的口号:**借助钉钉,致力于流程,让流程更简单**
|
||||
|
||||
## 我有话说
|
||||
开源离不开您的参与、支持与鼓励。开源不易,如果您觉得项目对您有帮助,请您动动小手star一下,也是对作者的最大帮助。
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
package com.snow.web.controller.common;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.fasterxml.uuid.impl.UUIDUtil;
|
||||
import com.snow.common.utils.uuid.IdUtils;
|
||||
import com.snow.common.utils.uuid.UUID;
|
||||
import com.snow.common.config.Global;
|
||||
import com.snow.common.constant.Constants;
|
||||
import com.snow.common.core.domain.AjaxResult;
|
||||
import com.snow.common.utils.StringUtils;
|
||||
import com.snow.common.utils.file.FileUtils;
|
||||
import com.snow.framework.storage.StorageService;
|
||||
import com.snow.system.domain.SysFile;
|
||||
import org.apache.logging.log4j.core.util.UuidUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -17,13 +15,9 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.snow.common.config.Global;
|
||||
import com.snow.common.config.ServerConfig;
|
||||
import com.snow.common.constant.Constants;
|
||||
import com.snow.common.core.domain.AjaxResult;
|
||||
import com.snow.common.utils.StringUtils;
|
||||
import com.snow.common.utils.file.FileUploadUtils;
|
||||
import com.snow.common.utils.file.FileUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 通用请求处理
|
||||
|
@ -35,8 +29,7 @@ public class CommonController
|
|||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
|
||||
|
||||
@Autowired
|
||||
private ServerConfig serverConfig;
|
||||
|
||||
|
||||
@Autowired
|
||||
private StorageService storageService;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package com.snow.web.controller.dingtalk;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dingtalk.oapi.lib.aes.DingTalkEncryptor;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.snow.common.constant.Constants;
|
||||
import com.snow.common.enums.DingTalkListenerType;
|
||||
import com.snow.dingtalk.common.EventNameEnum;
|
||||
|
@ -11,12 +14,16 @@ import com.snow.dingtalk.sync.SyncSysInfoFactory;
|
|||
import com.snow.system.domain.DingtalkCallBack;
|
||||
import com.snow.system.service.impl.DingtalkCallBackServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author qimingjin
|
||||
|
@ -85,4 +92,75 @@ public class DingTalkCallBackController {
|
|||
return Constants.CALL_BACK_FAIL_RETURN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收钉钉dingFlow机器人消息
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "/dingFlowRobot")
|
||||
public void dingFlowRobotCallback(@RequestBody(required = false) JSONObject body){
|
||||
log.info("dingFlowRobot"+body);
|
||||
|
||||
//todo 校验是否是钉钉群发送过来的消息
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 测试给钉钉群发消息
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args){
|
||||
|
||||
try {
|
||||
//钉钉机器人地址(配置机器人的webhook)
|
||||
String dingUrl = "";
|
||||
|
||||
//是否通知所有人
|
||||
boolean isAtAll = false;
|
||||
//通知具体人的手机号码列表
|
||||
List<String> mobileList = Lists.newArrayList();
|
||||
|
||||
//钉钉机器人消息内容
|
||||
String content ="TEST"+ "小哥,你好!";
|
||||
//组装请求内容
|
||||
String reqStr = buildReqStr(content, isAtAll, mobileList);
|
||||
|
||||
//推送消息(http请求)
|
||||
String result = HttpUtil.post(dingUrl, reqStr);
|
||||
System.out.println("result == " + result);
|
||||
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装请求报文
|
||||
* @param content
|
||||
* @return
|
||||
*/
|
||||
private static String buildReqStr(String content, boolean isAtAll, List<String> mobileList) {
|
||||
//消息内容
|
||||
Map<String, String> contentMap = Maps.newHashMap();
|
||||
contentMap.put("content", content);
|
||||
|
||||
//通知人
|
||||
Map<String, Object> atMap = Maps.newHashMap();
|
||||
//1.是否通知所有人
|
||||
atMap.put("isAtAll", isAtAll);
|
||||
//2.通知具体人的手机号码列表
|
||||
atMap.put("atMobiles", mobileList);
|
||||
|
||||
Map<String, Object> reqMap = Maps.newHashMap();
|
||||
reqMap.put("msgtype", "text");
|
||||
reqMap.put("text", contentMap);
|
||||
reqMap.put("at", atMap);
|
||||
|
||||
return JSON.toJSONString(reqMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,10 @@ public class SyncInitDataController {
|
|||
@Autowired
|
||||
private SysDeptServiceImpl sysDeptService;
|
||||
|
||||
|
||||
/**
|
||||
* 钉钉部门数据初始化到系统
|
||||
*/
|
||||
@GetMapping("/initDepartment")
|
||||
public void initDepartment(){
|
||||
List<OapiDepartmentListResponse.Department> dingTalkDepartmentList = departmentService.getDingTalkDepartmentList();
|
||||
|
|
|
@ -68,6 +68,7 @@ public class FlowController extends BaseController {
|
|||
* @return
|
||||
*/
|
||||
@PostMapping("/finishTask")
|
||||
@RequiresPermissions("system:flow:finishTask")
|
||||
@ResponseBody
|
||||
@RepeatSubmit
|
||||
public AjaxResult finishTask(CompleteTaskDTO completeTaskDTO)
|
||||
|
@ -89,8 +90,9 @@ public class FlowController extends BaseController {
|
|||
|
||||
return prefix+"/myTask";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的待办
|
||||
* 获取我的待办列表
|
||||
*/
|
||||
@RequiresPermissions("flow:get:todoList")
|
||||
@PostMapping("/findTasksByUserId")
|
||||
|
@ -101,6 +103,7 @@ public class FlowController extends BaseController {
|
|||
PageModel<TaskVO> taskList = flowableTaskService.findTasksByUserId(String.valueOf(userId), taskBaseDTO);
|
||||
return getFlowDataTable(taskList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有节点
|
||||
* @param processInstanceId
|
||||
|
@ -163,7 +166,7 @@ public class FlowController extends BaseController {
|
|||
}
|
||||
|
||||
/**
|
||||
* 我参与的任务
|
||||
* 跳转我的已办
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("flow:process:getMyTakePartInProcess")
|
||||
|
|
|
@ -52,7 +52,7 @@ public class FlowUserController extends BaseController {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* 获取用户组列表
|
||||
* @param filter
|
||||
*/
|
||||
@GetMapping(value = "/rest/getUserGroupList")
|
||||
|
|
|
@ -109,7 +109,11 @@ public class SysIndexController extends BaseController
|
|||
@GetMapping("/system/main")
|
||||
public String main(ModelMap mmap)
|
||||
{
|
||||
SysUser sysUser = ShiroUtils.getSysUser();
|
||||
mmap.put("version", Global.getVersion());
|
||||
//流程概况
|
||||
FlowGeneralSituationVO flowGeneralSituation = flowableService.getFlowGeneralSituation(String.valueOf(sysUser.getUserId()));
|
||||
mmap.put("flowGeneralSituation",flowGeneralSituation);
|
||||
return "main_v1";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.snow.web.controller.system;
|
||||
|
||||
import com.snow.framework.storage.StorageService;
|
||||
import com.snow.system.domain.SysFile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -42,6 +44,9 @@ public class SysProfileController extends BaseController
|
|||
@Autowired
|
||||
private SysPasswordService passwordService;
|
||||
|
||||
@Autowired
|
||||
private StorageService storageService;
|
||||
|
||||
/**
|
||||
* 个人信息
|
||||
*/
|
||||
|
@ -154,8 +159,8 @@ public class SysProfileController extends BaseController
|
|||
{
|
||||
if (!file.isEmpty())
|
||||
{
|
||||
String avatar = FileUploadUtils.upload(Global.getAvatarPath(), file);
|
||||
currentUser.setAvatar(avatar);
|
||||
SysFile store = storageService.store(file.getInputStream(), file.getSize(), file.getContentType(), file.getOriginalFilename());
|
||||
currentUser.setAvatar(store.getUrl());
|
||||
if (userService.updateUserInfo(currentUser) > 0)
|
||||
{
|
||||
ShiroUtils.setSysUser(userService.selectUserById(currentUser.getUserId()));
|
||||
|
|
|
@ -6,9 +6,9 @@ spring:
|
|||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://rm-bp1j1554xv1qs04295o.mysql.rds.aliyuncs.com:3306/ry?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: cloud_root
|
||||
password: Jin!152377
|
||||
url: jdbc:mysql://127.0.0.1:3306/snow?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: root
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
|
@ -31,6 +31,7 @@ spring:
|
|||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 30 KiB |
|
@ -19,56 +19,62 @@
|
|||
<div class="col-sm-3">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span class="label label-success pull-right">月</span>
|
||||
<h5>收入</h5>
|
||||
<!-- <span class="label label-success pull-right">月</span>-->
|
||||
<h5>待办任务数</h5>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins">40 886,200</h1>
|
||||
<div class="stat-percent font-bold text-success">98% <i class="fa fa-bolt"></i>
|
||||
</div>
|
||||
<small>总收入</small>
|
||||
<h1 class="no-margins" th:text="${flowGeneralSituation.todoTaskNum}"></h1>
|
||||
<div class="stat-percent font-bold text-success">
|
||||
<i class="fa fa-level-down"></i>
|
||||
<a onclick="toDoMyTask()">去完成</a>
|
||||
</div>
|
||||
<!--<small>总收入</small>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span class="label label-info pull-right">全年</span>
|
||||
<h5>订单</h5>
|
||||
<!-- <span class="label label-info pull-right">全年</span>-->
|
||||
<h5>已办任务数</h5>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins">275,800</h1>
|
||||
<div class="stat-percent font-bold text-info">20% <i class="fa fa-level-up"></i>
|
||||
<h1 class="no-margins" th:text="${flowGeneralSituation.doneTaskNum}"></h1>
|
||||
<div class="stat-percent font-bold text-info">
|
||||
<i class="fa fa-level-up"></i>
|
||||
<a onclick="toMyTakePartInProcess()">详情</a>
|
||||
</div>
|
||||
<small>新订单</small>
|
||||
<!--<small>新订单</small>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span class="label label-primary pull-right">今天</span>
|
||||
<h5>访客</h5>
|
||||
<!-- <span class="label label-primary pull-right">今天</span>-->
|
||||
<h5>发起流程数</h5>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins">106,120</h1>
|
||||
<div class="stat-percent font-bold text-navy">44% <i class="fa fa-level-up"></i>
|
||||
<h1 class="no-margins" th:text="${flowGeneralSituation.myStartProcessInstanceNum}"></h1>
|
||||
<div class="stat-percent font-bold text-navy">
|
||||
<i class="fa fa-level-up"></i>
|
||||
</div>
|
||||
<small>新访客</small>
|
||||
<!-- <small>新访客</small>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span class="label label-danger pull-right">最近一个月</span>
|
||||
<h5>活跃用户</h5>
|
||||
<!--<span class="label label-danger pull-right">最近一个月</span>-->
|
||||
<h5>任务超时数(3天)</h5>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins">80,600</h1>
|
||||
<div class="stat-percent font-bold text-danger">38% <i class="fa fa-level-down"></i>
|
||||
<h1 class="no-margins" th:text="${flowGeneralSituation.threeTodoTaskNum}"></h1>
|
||||
<div class="stat-percent font-bold text-danger">
|
||||
<i class="fa fa-level-down"></i>
|
||||
</div>
|
||||
<small>12月</small>
|
||||
<!-- <small>12月</small>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -198,6 +204,16 @@
|
|||
var preViewUrl=prefixFlow+'/toFinishTask?taskId='+taskId;
|
||||
$.modal.openTab("审批", preViewUrl);
|
||||
}
|
||||
|
||||
function toDoMyTask() {
|
||||
var preViewUrl=prefixFlow+'/toDoMyTask?';
|
||||
$.modal.openTab("我的待办任务", preViewUrl);
|
||||
}
|
||||
|
||||
function toMyTakePartInProcess() {
|
||||
var preViewUrl=prefixFlow+'/toMyTakePartInProcess?';
|
||||
$.modal.openTab("我的已办任务", preViewUrl);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -4,258 +4,258 @@
|
|||
<th:block th:include="include :: header('修改用户头像')" />
|
||||
<th:block th:include="include :: cropper-css" />
|
||||
<style type='text/css'>
|
||||
/* avator css start */
|
||||
.container {
|
||||
margin: 10px 5px 5px 5px;
|
||||
}
|
||||
|
||||
.action {
|
||||
padding: 5px 0px;
|
||||
}
|
||||
|
||||
.cropped {
|
||||
width: 200px;
|
||||
height: 345px;
|
||||
border: 1px #ddd solid;
|
||||
box-shadow: 0px 0px 12px #ddd;
|
||||
}
|
||||
|
||||
.img-preview {
|
||||
border-radius: 50%;
|
||||
box-shadow: 0px 0px 12px #7e7e7e;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.preview-box {
|
||||
text-align: center;
|
||||
margin: 0px auto;
|
||||
margin-top: 10px;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.preview-md {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
|
||||
.preview-sm {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
|
||||
.preview-xs {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.imageBox {
|
||||
border: 1px solid #aaa;
|
||||
overflow: hidden;
|
||||
cursor: move;
|
||||
box-shadow: 4px 4px 12px #B0B0B0;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.btn-custom {
|
||||
float: right;
|
||||
width: 46px;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
height: 37px;
|
||||
line-height: 37px;
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
margin: 0px 2px;
|
||||
background-color: #f38e81;
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 5px #B0B0B0;
|
||||
border: 0px #fff solid;
|
||||
}
|
||||
/*选择文件上传*/
|
||||
.new-contentarea {
|
||||
width: 165px;
|
||||
overflow: hidden;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.new-contentarea label {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.new-contentarea input[type=file] {
|
||||
width: 188px;
|
||||
height: 60px;
|
||||
background: #333;
|
||||
margin: 0 auto;
|
||||
position: absolute;
|
||||
right: 50%;
|
||||
margin-right: -94px;
|
||||
top: 0;
|
||||
right/*\**/: 0px\9;
|
||||
margin-right/*\**/: 0px\9;
|
||||
width/*\**/: 10px\9;
|
||||
opacity: 0;
|
||||
filter: alpha(opacity=0);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
a.upload-img {
|
||||
width: 165px;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
height: 37px;
|
||||
line-height: 37px;
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
background-color: #f38e81;
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
border: 0px #fff solid;
|
||||
box-shadow: 0px 0px 5px #B0B0B0;
|
||||
}
|
||||
|
||||
a.upload-img:hover {
|
||||
background-color: #ec7e70;
|
||||
}
|
||||
|
||||
.tc {
|
||||
text-align: center;
|
||||
}
|
||||
/* avator css end */
|
||||
/* avator css start */
|
||||
.container {
|
||||
margin: 10px 5px 5px 5px;
|
||||
}
|
||||
|
||||
.action {
|
||||
padding: 5px 0px;
|
||||
}
|
||||
|
||||
.cropped {
|
||||
width: 200px;
|
||||
border: 1px #ddd solid;
|
||||
box-shadow: 0px 0px 12px #ddd;
|
||||
}
|
||||
|
||||
.img-preview {
|
||||
border-radius: 50%;
|
||||
box-shadow: 0px 0px 12px #7e7e7e;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.preview-box {
|
||||
text-align: center;
|
||||
margin: 0px auto;
|
||||
margin-top: 10px;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.preview-md {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
|
||||
.preview-sm {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
|
||||
.preview-xs {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.imageBox {
|
||||
border: 1px solid #aaa;
|
||||
overflow: hidden;
|
||||
cursor: move;
|
||||
box-shadow: 4px 4px 12px #B0B0B0;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.btn-custom {
|
||||
float: right;
|
||||
width: 46px;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
height: 37px;
|
||||
line-height: 37px;
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
margin: 0px 2px;
|
||||
background-color: #f38e81;
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 5px #B0B0B0;
|
||||
border: 0px #fff solid;
|
||||
}
|
||||
/*选择文件上传*/
|
||||
.new-contentarea {
|
||||
width: 165px;
|
||||
overflow: hidden;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.new-contentarea label {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.new-contentarea input[type=file] {
|
||||
width: 188px;
|
||||
height: 60px;
|
||||
background: #333;
|
||||
margin: 0 auto;
|
||||
position: absolute;
|
||||
right: 50%;
|
||||
margin-right: -94px;
|
||||
top: 0;
|
||||
right/*\**/: 0px\9;
|
||||
margin-right/*\**/: 0px\9;
|
||||
width/*\**/: 10px\9;
|
||||
opacity: 0;
|
||||
filter: alpha(opacity=0);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
a.upload-img {
|
||||
width: 165px;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
height: 37px;
|
||||
line-height: 37px;
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
background-color: #f38e81;
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
border: 0px #fff solid;
|
||||
box-shadow: 0px 0px 5px #B0B0B0;
|
||||
}
|
||||
|
||||
a.upload-img:hover {
|
||||
background-color: #ec7e70;
|
||||
}
|
||||
|
||||
.tc {
|
||||
text-align: center;
|
||||
}
|
||||
/* avator css end */
|
||||
</style>
|
||||
</head>
|
||||
<body class="white-bg">
|
||||
<div class="row container">
|
||||
<div class="col-md-10">
|
||||
<div class="imageBox">
|
||||
<img id="avatar" th:src="(${user.avatar} == '') ? @{/img/profile.jpg} : @{${user.avatar}}" >
|
||||
</div>
|
||||
<div class="action">
|
||||
<div class="new-contentarea tc">
|
||||
<a href="javascript:void(0)" class="upload-img"><label for="inputImage">上传图像</label> </a>
|
||||
<input type="file" name="avatar" id="inputImage" accept="image/*"/>
|
||||
</div>
|
||||
<button type="button" class="btn-custom" data-method="zoom" data-option="0.1"><i class="fa fa-search-plus"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="zoom" data-option="-0.1"><i class="fa fa-search-minus"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="rotate" data-option="-45"><i class="fa fa-rotate-left"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="rotate" data-option="45"><i class="fa fa-rotate-right"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="scaleX" data-option="-1"><i class="fa fa-arrows-h"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="scaleY" data-option="-1"><i class="fa fa-arrows-v"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="reset"><i class="fa fa-refresh"></i></button>
|
||||
</div>
|
||||
<div class="row container">
|
||||
<div class="col-md-10">
|
||||
<div class="imageBox">
|
||||
<img id="avatar" th:src="(${#strings.isEmpty(user.avatar)}) ? @{/img/profile.jpg} : @{${user.avatar}}" th:onerror="'this.src=\'' + @{'/img/profile.jpg'} + '\''">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="cropped">
|
||||
<div class="preview-box">
|
||||
<div class="img-preview preview-xs"></div>
|
||||
</div>
|
||||
<div class="preview-box">
|
||||
<div class="img-preview preview-sm"></div>
|
||||
</div>
|
||||
<div class="preview-box">
|
||||
<div class="img-preview preview-md"></div>
|
||||
</div>
|
||||
<div class="action">
|
||||
<div class="new-contentarea tc">
|
||||
<a href="javascript:void(0)" class="upload-img"><label for="inputImage">上传图像</label> </a>
|
||||
<input type="file" name="avatar" id="inputImage" accept="image/*"/>
|
||||
</div>
|
||||
<button type="button" class="btn-custom" data-method="zoom" data-option="0.1"><i class="fa fa-search-plus"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="zoom" data-option="-0.1"><i class="fa fa-search-minus"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="rotate" data-option="-45"><i class="fa fa-rotate-left"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="rotate" data-option="45"><i class="fa fa-rotate-right"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="scaleX" data-option="-1"><i class="fa fa-arrows-h"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="scaleY" data-option="-1"><i class="fa fa-arrows-v"></i></button>
|
||||
<button type="button" class="btn-custom" data-method="reset"><i class="fa fa-refresh"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="cropped">
|
||||
<div class="preview-box">
|
||||
<div class="img-preview preview-xs"></div>
|
||||
</div>
|
||||
<div class="preview-box">
|
||||
<div class="img-preview preview-sm"></div>
|
||||
</div>
|
||||
<div class="preview-box">
|
||||
<div class="img-preview preview-md"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
<th:block th:include="include :: cropper-js" />
|
||||
<script type="text/javascript">
|
||||
var cropper;
|
||||
var croppable = false;
|
||||
$(window).load(function() {
|
||||
var image = document.getElementById('avatar');
|
||||
cropper = new Cropper(image, {
|
||||
aspectRatio: 1,
|
||||
viewMode: 1,
|
||||
autoCropArea: 0.9,
|
||||
preview: '.img-preview',
|
||||
ready: function () {
|
||||
croppable = true;
|
||||
}
|
||||
})
|
||||
var cropper;
|
||||
var croppable = false;
|
||||
$(window).load(function() {
|
||||
var image = document.getElementById('avatar');
|
||||
cropper = new Cropper(image, {
|
||||
aspectRatio: 1,
|
||||
viewMode: 1,
|
||||
autoCropArea: 0.9,
|
||||
preview: '.img-preview',
|
||||
ready: function () {
|
||||
croppable = true;
|
||||
}
|
||||
})
|
||||
|
||||
$('#inputImage').on('change', function() {
|
||||
var reader = new FileReader();
|
||||
var file = $('#inputImage')[0].files[0];
|
||||
if (/^image\/\w+$/.test(file.type)) {
|
||||
reader.onload = function(e) {
|
||||
if(croppable){
|
||||
cropper.replace(e.target.result)
|
||||
}
|
||||
}
|
||||
reader.readAsDataURL(this.files[0]);
|
||||
} else {
|
||||
$.modal.alertWarning('请选择一个图片文件。');
|
||||
}
|
||||
});
|
||||
$('#inputImage').on('change', function() {
|
||||
var reader = new FileReader();
|
||||
var file = $('#inputImage')[0].files[0];
|
||||
if (/^image\/\w+$/.test(file.type)) {
|
||||
reader.onload = function(e) {
|
||||
if(croppable){
|
||||
cropper.replace(e.target.result)
|
||||
}
|
||||
}
|
||||
reader.readAsDataURL(this.files[0]);
|
||||
} else {
|
||||
$.modal.alertWarning('请选择一个图片文件。');
|
||||
}
|
||||
});
|
||||
|
||||
$('.btn-custom').on('click',function (e) {
|
||||
if (!croppable) {
|
||||
$.modal.alertWarning("裁剪框加载中,请稍后...");
|
||||
return;
|
||||
}
|
||||
var data = {
|
||||
method: $(this).data('method'),
|
||||
option: $(this).data('option') || undefined,
|
||||
};
|
||||
var result = cropper[data.method](data.option, data.secondOption);
|
||||
if(['scaleX','scaleY'].indexOf(data.method) !== -1){
|
||||
$(this).data('option', -data.option)
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function submitHandler() {
|
||||
if (!croppable) {
|
||||
$.modal.alertWarning("裁剪框加载中,请稍后...");
|
||||
return
|
||||
}
|
||||
cropper.getCroppedCanvas().toBlob(function(img) {
|
||||
var formdata = new FormData();
|
||||
formdata.append("avatarfile", img);
|
||||
$.ajax({
|
||||
url: ctx + "system/user/profile/updateAvatar",
|
||||
data: formdata,
|
||||
type: "post",
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(result) {
|
||||
$.operate.saveSuccess(result);
|
||||
$('.btn-custom').on('click',function (e) {
|
||||
if (!croppable) {
|
||||
$.modal.alertWarning("裁剪框加载中,请稍后...");
|
||||
return;
|
||||
}
|
||||
var data = {
|
||||
method: $(this).data('method'),
|
||||
option: $(this).data('option') || undefined,
|
||||
};
|
||||
var result = cropper[data.method](data.option, data.secondOption);
|
||||
if(['scaleX','scaleY'].indexOf(data.method) !== -1){
|
||||
$(this).data('option', -data.option)
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
$(window).resize(function() {
|
||||
$('.imageBox').height($(window).height() - 80);
|
||||
}).resize();
|
||||
|
||||
if (!HTMLCanvasElement.prototype.toBlob) {
|
||||
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
|
||||
value: function(callback, type, quality) {
|
||||
var canvas = this;
|
||||
setTimeout(function() {
|
||||
var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]);
|
||||
var len = binStr.length;
|
||||
var arr = new Uint8Array(len);
|
||||
for (var i = 0; i < len; i++) {
|
||||
arr[i] = binStr.charCodeAt(i);
|
||||
}
|
||||
callback(new Blob([arr], {
|
||||
type: type || 'image/png'
|
||||
}));
|
||||
});
|
||||
function submitHandler() {
|
||||
if (!croppable) {
|
||||
$.modal.alertWarning("裁剪框加载中,请稍后...");
|
||||
return
|
||||
}
|
||||
});
|
||||
}
|
||||
cropper.getCroppedCanvas().toBlob(function(img) {
|
||||
var formdata = new FormData();
|
||||
formdata.append("avatarfile", img);
|
||||
$.ajax({
|
||||
url: ctx + "system/user/profile/updateAvatar",
|
||||
data: formdata,
|
||||
type: "post",
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(result) {
|
||||
$.operate.saveSuccess(result);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
$(window).resize(function() {
|
||||
$('.imageBox').height($(window).height() - 80);
|
||||
$('.cropped').height($(window).height() - 40);
|
||||
}).resize();
|
||||
|
||||
if (!HTMLCanvasElement.prototype.toBlob) {
|
||||
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
|
||||
value: function(callback, type, quality) {
|
||||
var canvas = this;
|
||||
setTimeout(function() {
|
||||
var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]);
|
||||
var len = binStr.length;
|
||||
var arr = new Uint8Array(len);
|
||||
for (var i = 0; i < len; i++) {
|
||||
arr[i] = binStr.charCodeAt(i);
|
||||
}
|
||||
callback(new Blob([arr], {
|
||||
type: type || 'image/png'
|
||||
}));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -64,7 +64,7 @@ public class SendMessageEventLister extends AbstractEventListener {
|
|||
protected void taskCreated(FlowableEngineEntityEvent event) {
|
||||
//任务创建可发送短信,邮件通知接收人(代办人)
|
||||
log.info("ManagerTaskEventListener----taskCreated任务创建监听:{}",JSON.toJSONString(event));
|
||||
// sendDingTalkMessage(event);
|
||||
sendDingTalkMessage(event);
|
||||
//todo 邮件通知
|
||||
}
|
||||
|
||||
|
|
|
@ -449,6 +449,7 @@ public class FlowableServiceImpl implements FlowableService {
|
|||
public ProcessInstance getProcessInstanceById(String id){
|
||||
return runtimeService.createProcessInstanceQuery()
|
||||
.processInstanceId(id)
|
||||
.includeProcessVariables()
|
||||
.singleResult();
|
||||
}
|
||||
|
||||
|
@ -669,6 +670,8 @@ public class FlowableServiceImpl implements FlowableService {
|
|||
historicProcessInstanceQuery.includeProcessVariables();
|
||||
return historicProcessInstanceQuery;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 赋值ProcessInstanceVOs
|
||||
* @param processInstanceVOS
|
||||
|
@ -698,6 +701,7 @@ public class FlowableServiceImpl implements FlowableService {
|
|||
t.setStartUserName(sysUser.getUserName());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageModel<HistoricTaskInstanceVO> getHistoricTaskInstance(HistoricTaskInstanceDTO historicTaskInstanceDTO) {
|
||||
HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package com.snow.framework.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* @program: snow
|
||||
* @description 允许跨域
|
||||
* @author: 没用的阿吉
|
||||
* @create: 2021-02-27 13:28
|
||||
**/
|
||||
@Configuration
|
||||
public class CrossConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
|
||||
registry.addMapping("/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("GET","HEAD","POST","PUT","DELETE","OPTIONS")
|
||||
.allowCredentials(true)
|
||||
.maxAge(3600)
|
||||
.allowedHeaders("*");
|
||||
}
|
||||
}
|
|
@ -282,6 +282,7 @@ public class ShiroConfig
|
|||
filterChainDefinitionMap.put("/modeler/**", "anon");
|
||||
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
|
||||
filterChainDefinitionMap.put("/dingTalk/dingTalkCallBack", "anon");
|
||||
filterChainDefinitionMap.put("/dingTalk/dingFlowRobot", "anon");
|
||||
// 退出 logout地址,shiro去清除session
|
||||
filterChainDefinitionMap.put("/logout", "logout");
|
||||
// 不需要拦截的访问
|
||||
|
|
Loading…
Reference in New Issue