Commit 50692090 by zhangdaihao

Jeecg-Boot 2.1.0 版本发布,Online表单开发&在线代码生成器(迟到的版本)

parent 83935778
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
Jeecg-Boot 快速开发平台(前后端分离版本) Jeecg-Boot 快速开发平台(前后端分离版本)
=============== ===============
当前最新版本: 2.0.2(发布日期:20190708 当前最新版本: 2.1.0(发布日期:20190826
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE) [![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-JEECG团队-orange.svg)](http://www.jeecg.com) [![](https://img.shields.io/badge/Author-JEECG团队-orange.svg)](http://www.jeecg.com)
...@@ -239,7 +239,7 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤 ...@@ -239,7 +239,7 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤
│ └─异常页面 │ └─异常页面
│ └─个人页面 │ └─个人页面
├─Online在线开发(暂未开源) ├─Online在线开发(暂未开源)
│ ├─Online在线表单 │ ├─Online在线表单 - 已开放功能
│ ├─Online在线图表 │ ├─Online在线图表
│ ├─Online图表模板配置 │ ├─Online图表模板配置
│ ├─Online在线报表 │ ├─Online在线报表
......
Ant Design Jeecg Vue Ant Design Jeecg Vue
==== ====
当前最新版本: 2.0.2(发布日期:20190708 当前最新版本: 2.1.0(发布日期:20190826
Overview Overview
---- ----
......
{ {
"name": "ant-design-vue-jeecg", "name": "vue-antd-jeecg",
"version": "2.0.2", "version": "2.1.0",
"private": true, "private": false,
"scripts": { "scripts": {
"serve": "vue-cli-service serve --open", "pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"lint": "vue-cli-service lint", "lint": "vue-cli-service lint"
"test:unit": "vue-cli-service test:unit",
"test:e2e": "vue-cli-service test:e2e"
}, },
"dependencies": { "dependencies": {
"@antv/data-set": "^0.10.2", "@antv/data-set": "^0.10.2",
"@jeecg/antd-onine": "^1.0.1",
"@tinymce/tinymce-vue": "^2.0.0", "@tinymce/tinymce-vue": "^2.0.0",
"ant-design-vue": "^1.3.9", "ant-design-vue": "^1.3.9",
"apexcharts": "^3.6.5", "apexcharts": "^3.6.5",
......
...@@ -242,9 +242,10 @@ ...@@ -242,9 +242,10 @@
<!-- 全局配置 --> <!-- 全局配置 -->
<script> <script>
window._CONFIG = {}; window._CONFIG = {};
window._CONFIG['domianURL'] = 'http://localhost:8080/jeecg-boot'; window._CONFIG['domianURL'] = 'http://127.0.0.1:8080/jeecg-boot';
window._CONFIG['imgDomainURL'] = 'http://localhost:8080/jeecg-boot/sys/common/view'; window._CONFIG['casPrefixUrl'] = 'http://cas.example.org:8443/cas';
window._CONFIG['pdfDomainURL'] = 'http://localhost:8080/jeecg-boot/sys/common/pdf/pdfPreviewIframe'; window._CONFIG['imgDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/view';
window._CONFIG['pdfDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/pdf/pdfPreviewIframe';
</script> </script>
</head> </head>
...@@ -261,15 +262,6 @@ ...@@ -261,15 +262,6 @@
</div> </div>
</div> </div>
<!-- update_begin author:sunjianlei date:20190524 for: 去指定页面的加载动画 -->
<script>
// 去指定页面的加载动画
if (location.href.indexOf('online/desform/pureview') !== -1) {
document.getElementById('loader-wrapper').style.display = 'none'
}
</script>
<!-- update_end author:sunjianlei date:20190524 for: 去指定页面的加载动画 -->
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -45,11 +45,11 @@ const getPermissionRuleList = (params)=>getAction("/sys/permission/getPermRuleLi ...@@ -45,11 +45,11 @@ const getPermissionRuleList = (params)=>getAction("/sys/permission/getPermRuleLi
const queryPermissionRule = (params)=>getAction("/sys/permission/queryPermissionRule",params); const queryPermissionRule = (params)=>getAction("/sys/permission/queryPermissionRule",params);
// 部门管理 // 部门管理
const queryDepartTreeList = (params)=>getAction("/sysdepart/sysDepart/queryTreeList",params); const queryDepartTreeList = (params)=>getAction("/sys/sysDepart/queryTreeList",params);
const queryIdTree = (params)=>getAction("/sysdepart/sysDepart/queryIdTree",params); const queryIdTree = (params)=>getAction("/sys/sysDepart/queryIdTree",params);
const queryParentName = (params)=>getAction("/sysdepart/sysDepart/queryParentName",params); const queryParentName = (params)=>getAction("/sys/sysDepart/queryParentName",params);
const searchByKeywords = (params)=>getAction("/sysdepart/sysDepart/searchBy",params); const searchByKeywords = (params)=>getAction("/sys/sysDepart/searchBy",params);
const deleteByDepartId = (params)=>deleteAction("/sysdepart/sysDepart/delete",params); const deleteByDepartId = (params)=>deleteAction("/sys/sysDepart/delete",params);
//日志管理 //日志管理
//const getLogList = (params)=>getAction("/sys/log/list",params); //const getLogList = (params)=>getAction("/sys/log/list",params);
......
import Vue from 'vue'
import { ACCESS_TOKEN } from "@/store/mutation-types"
import store from '@/store'
/**
* 单点登录
*/
const init = (callback) => {
console.log("-------单点登录开始-------");
let token = Vue.ls.get(ACCESS_TOKEN);
let st = getUrlParam("ticket");
var sevice = "http://"+window.location.host+"/";
if(token){
loginSuccess(callback);
}else{
if(st){
validateSt(st,sevice,callback);
}else{
var serviceUrl = encodeURIComponent(sevice);
window.location.href = window._CONFIG['casPrefixUrl']+"/login?service="+serviceUrl;
}
}
console.log("-------单点登录结束-------");
};
const SSO = {
init: init
};
function getUrlParam(paraName) {
var url = document.location.toString();
var arrObj = url.split("?");
if (arrObj.length > 1) {
var arrPara = arrObj[1].split("&");
var arr;
for (var i = 0; i < arrPara.length; i++) {
arr = arrPara[i].split("=");
if (arr != null && arr[0] == paraName) {
return arr[1];
}
}
return "";
}
else {
return "";
}
}
function validateSt(ticket,service,callback){
let params = {
ticket: ticket,
service:service
};
store.dispatch('ValidateLogin',params).then(res => {
//this.departConfirm(res)
if(res.success){
loginSuccess(callback);
}else{
var sevice = "http://"+window.location.host+"/";
var serviceUrl = encodeURIComponent(sevice);
window.location.href = window._CONFIG['casPrefixUrl']+"/login?service="+serviceUrl;
}
}).catch((err) => {
console.log(err);
//that.requestFailed(err);
});
}
function loginSuccess (callback) {
callback();
}
export default SSO;
\ No newline at end of file
...@@ -39,3 +39,5 @@ UserMenu.vue:首页右上侧的内容 ...@@ -39,3 +39,5 @@ UserMenu.vue:首页右上侧的内容
![输入图片说明](https://static.oschina.net/uploads/img/201904/12201226_laQK.png "在这里输入图片标题") ![输入图片说明](https://static.oschina.net/uploads/img/201904/12201226_laQK.png "在这里输入图片标题")
####16.trend包 趋势显示组件(如下图) ####16.trend包 趋势显示组件(如下图)
![输入图片说明](https://static.oschina.net/uploads/img/201904/12201600_Wo8K.png "在这里输入图片标题") ![输入图片说明](https://static.oschina.net/uploads/img/201904/12201600_Wo8K.png "在这里输入图片标题")
![corn表达式](https://oscimg.oschina.net/oscnet/661f9ac09016395f9f49286143af3241623.jpg)
![corn控件添加清除按钮](https://oscimg.oschina.net/oscnet/15096e49f2e29bd829e304d56770025d03c.jpg)
\ No newline at end of file
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
type="arc" type="arc"
:zIndex="1" :zIndex="1"
:start="arcGuide2Start" :start="arcGuide2Start"
:end="getArcGuide2End" :end="getArcGuide2End()"
:vStyle="arcGuide2Style" :vStyle="arcGuide2Style"
></v-guide> ></v-guide>
<v-guide <v-guide
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
}]; }];
const data = [ const data = [
{ value: 7.0 }, { value: 0},
]; ];
export default { export default {
...@@ -96,7 +96,7 @@ ...@@ -96,7 +96,7 @@
props:{ props:{
datasource:{ datasource:{
type: Number, type: Number,
default:7 default:0
}, },
title: { title: {
type: String, type: String,
......
<template>
<div class="components-input-demo-presuffix">
<a-input @click="openModal" placeholder="corn表达式" v-model="cron" @change="handleOK">
<a-icon slot="prefix" type="schedule" title="corn控件"/>
<a-icon v-if="cron" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
</a-input>
<JCronModal ref="innerVueCron" :data="cron" @ok="handleOK"></JCronModal>
</div>
</template>
<script>
import JCronModal from "./modal/JCronModal";
export default {
name: 'JCron',
components: {
JCronModal
},
props: {
value: {
required: false,
type: String,
default:()=>{
return '* * * * * ? *'
}
}
},
data(){
return {
cron: this.value,
}
},
watch:{
value(val){
this.cron = val
}
},
methods:{
openModal(){
this.$refs.innerVueCron.show();
},
handleOK(val){
this.cron = val;
this.$emit("change", this.cron);
//this.$emit("change", Object.assign({}, this.cron));
},
handleEmpty(){
this.handleOK('')
}
},
model: {
prop: 'value',
event: 'change'
}
}
</script>
<style scoped>
.components-input-demo-presuffix .anticon-close-circle {
cursor: pointer;
color: #ccc;
transition: color 0.3s;
font-size: 12px;
}
.components-input-demo-presuffix .anticon-close-circle:hover {
color: #f5222d;
}
.components-input-demo-presuffix .anticon-close-circle:active {
color: #666;
}
</style>
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
}, },
watch: { watch: {
value(newValue) { value(newValue) {
this.myValue = newValue this.myValue = (newValue == null ? '' : newValue)
}, },
myValue(newValue) { myValue(newValue) {
console.log(newValue) console.log(newValue)
......
...@@ -2,30 +2,33 @@ ...@@ -2,30 +2,33 @@
## 参数配置 ## 参数配置
| 参数 | 类型 | 必填 | 说明 | | 参数 | 类型 | 必填 | 说明 |
|--------------|---------|------|----------------------------------------------------------------| |--------------|---------|------|---------------------------------------------------------------------------------|
| columns | array | ✔️ | 表格列的配置描述,具体项见下表 | | columns | array | ✔️ | 表格列的配置描述,具体项见下表 |
| dataSource | array | ✔️ | 表格数据 | | dataSource | array | ✔️ | 表格数据 |
| loading | boolean | | 是否正在加载,加载中不会显示任何行,默认false | | loading | boolean | | 是否正在加载,加载中不会显示任何行,默认false |
| actionButton | boolean | | 是否显示操作按钮,包括"新增"、"删除",默认false | | actionButton | boolean | | 是否显示操作按钮,包括"新增"、"删除",默认false |
| rowNumber | boolean | | 是否显示行号,默认false | | rowNumber | boolean | | 是否显示行号,默认false |
| rowSelection | boolean | | 是否可选择行,默认false | | rowSelection | boolean | | 是否可选择行,默认false |
| maxHeight | number | | 设定最大高度(px),默认400 | | dragSort | boolean | | 是否可拖动排序,默认false |
| disabledRows | object | | 设定禁用的行,被禁用的行无法被选择和编辑,配置方法可以查看示例 | | dragSortKey | string | | 拖动排序存储的Key,无需定义在columns内也能在getValues()时获取到值,默认orderNum |
| disabled | boolean | | 是否禁用所有行,默认false | | maxHeight | number | | 设定最大高度(px),默认400 |
| disabledRows | object | | 设定禁用的行,被禁用的行无法被选择和编辑,配置方法可以查看示例 |
| disabled | boolean | | 是否禁用所有行,默认false |
### columns 参数详解 ### columns 参数详解
| 参数 | 类型 | 必填 | 说明 | | 参数 | 类型 | 必填 | 说明 |
|---------------|--------|------|--------------------------------------------------------------------------------------------------------------------------------------------------------| |---------------|---------|------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
| title | string | ✔️ | 表格列头显示的问题 | | title | string | ✔️ | 表格列头显示的问题 |
| key | string | ✔️ | 列数据在数据项中对应的 key,必须是唯一的 | | key | string | ✔️ | 列数据在数据项中对应的 key,必须是唯一的 |
| type | string | ✔️ | 表单的类型,可以通过`JEditableTableUtil.FormTypes`赋值 | | type | string | ✔️ | 表单的类型,可以通过`JEditableTableUtil.FormTypes`赋值 |
| width | string | | 列的宽度,可以是百分比,也可以是`px`或其他单位,建议设置为百分比,且每一列的宽度加起来不应超过100%,否则可能会不能达到预期的效果。留空会自动计算百分比 | | width | string | | 列的宽度,可以是百分比,也可以是`px`或其他单位,建议设置为百分比,且每一列的宽度加起来不应超过100%,否则可能会不能达到预期的效果。留空会自动计算百分比 |
| placeholder | string | | 表单预期值的提示信息,可以使用`${...}`变量替换文本(详见`${...} 变量使用方式`) | | placeholder | string | | 表单预期值的提示信息,可以使用`${...}`变量替换文本(详见`${...} 变量使用方式`) |
| defaultValue | string | | 默认值,在新增一行时生效 | | defaultValue | string | | 默认值,在新增一行时生效 |
| validateRules | array | | 表单验证规则,配置方式见[validateRules 配置规则](#validaterules-配置规则) | | validateRules | array | | 表单验证规则,配置方式见[validateRules 配置规则](#validaterules-配置规则) |
| props | object | | 设置添加给表单元素的自定义属性,例如:`props:{title: 'show title'}` | | props | object | | 设置添加给表单元素的自定义属性,例如:`props:{title: 'show title'}` |
| disabled | boolean | | 是否禁用当前列,默认false |
#### 当 type=checkbox 时所需的参数 #### 当 type=checkbox 时所需的参数
...@@ -40,7 +43,7 @@ ...@@ -40,7 +43,7 @@
|------------|---------|------|----------------------------------------------------| |------------|---------|------|----------------------------------------------------|
| options | array | ✔️ | 下拉选项列表,详见下表 | | options | array | ✔️ | 下拉选项列表,详见下表 |
| allowInput | boolean | | 是否允许用户输入内容,并创建新的内容 | | allowInput | boolean | | 是否允许用户输入内容,并创建新的内容 |
| dictCode | String | | 数据字典Code,若options也有值,则拼接options后面 | | dictCode | String | | 数据字典Code,若options也有值,则拼接options后面 |
##### options 所需参数 ##### options 所需参数
...@@ -75,11 +78,12 @@ ...@@ -75,11 +78,12 @@
## 事件 ## 事件
| 事件名 | 触发时机 | 参数 | | 事件名 | 触发时机 | 参数 |
|-----------------|----------------------------------------------------|-------------------------------| |-----------------|----------------------------------------------------|--------------------------------------------------|
| added | 当添加行操作完成后触发 | | | added | 当添加行操作完成后触发 | |
| deleted | 当删除行操作完成后触发(批量删除操作只会触发一次) | `deleteIds` 被逻辑删除的id | | deleted | 当删除行操作完成后触发(批量删除操作只会触发一次) | `deleteIds` 被逻辑删除的id |
| selectRowChange | 当行被选中或取消选中时触发 | `selectedRowIds` 被选中行的id | | selectRowChange | 当行被选中或取消选中时触发 | `selectedRowIds` 被选中行的id |
| valueChange | 当数据发生改变的时候触发的事件 | `{ type, row, column, value, target }` Event对象 |
## 方法 ## 方法
...@@ -490,6 +494,7 @@ this.$refs.editableTable.getValues((error, values) => { ...@@ -490,6 +494,7 @@ this.$refs.editableTable.getValues((error, values) => {
/* a 标签的点击事件,删除当前选中的行 */ /* a 标签的点击事件,删除当前选中的行 */
handleDelete(props) { handleDelete(props) {
// 参数解释 // 参数解释
// props.index :当前行的下标
// props.text :当前值,可能是defaultValue定义的值,也可能是从dataSource中取出的值 // props.text :当前值,可能是defaultValue定义的值,也可能是从dataSource中取出的值
// props.rowId :当前选中行的id,如果是新增行则是临时id // props.rowId :当前选中行的id,如果是新增行则是临时id
// props.column :当前操作的列 // props.column :当前操作的列
......
...@@ -58,6 +58,16 @@ ...@@ -58,6 +58,16 @@
watch:{ watch:{
departId(){ departId(){
this.initDepartComponent() this.initDepartComponent()
},
visible: {
handler() {
if (this.departId) {
this.checkedKeys = this.departId.split(",");
console.log('this.departId', this.departId)
} else {
this.checkedKeys = [];
}
}
} }
}, },
methods:{ methods:{
......
...@@ -148,11 +148,28 @@ ...@@ -148,11 +148,28 @@
<style lang="scss"> <style lang="scss">
.ant-menu.ant-menu-root { .ant-menu.ant-menu-root {
& > .ant-menu-item:first-child { & > .ant-menu-item:first-child {
background-color: white; background-color: transparent;
& > a, & > a:hover { & > a, & > a:hover {
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.65);
}
&.ant-menu-item-selected {
& > a, & > a:hover {
color: #1890ff;
}
}
}
&.ant-menu-dark > .ant-menu-item:first-child {
& > a, & > a:hover {
color: rgba(255, 255, 255, 0.65);
}
&.ant-menu-item-selected {
& > a, & > a:hover {
color: rgba(255, 255, 255, 1);
}
} }
} }
} }
......
...@@ -153,9 +153,10 @@ ...@@ -153,9 +153,10 @@
</a-alert> </a-alert>
</div> </div>
</div> </div>
<div class="setting-drawer-index-handle" @click="toggle"> <div class="setting-drawer-index-handle" @click="toggle" v-if="visible">
<a-icon type="setting" v-if="!visible"/> <!-- <a-icon type="setting" v-if="!visible"/>-->
<a-icon type="close" v-else/> <!-- <a-icon type="close" v-else/>-->
<a-icon type="close" />
</div> </div>
</a-drawer> </a-drawer>
</div> </div>
......
<template> <template>
<a-modal <a-modal
:title="title" :title="currTitle"
:width="450" :width="450"
:visible="visible" :visible="visible"
:closable="false" :closable="false"
...@@ -41,6 +41,9 @@ ...@@ -41,6 +41,9 @@
<script> <script>
import { getAction,putAction } from '@/api/manage' import { getAction,putAction } from '@/api/manage'
import Vue from 'vue'
import store from '@/store/'
import { USER_INFO } from "@/store/mutation-types"
export default { export default {
name: 'DepartSelect', name: 'DepartSelect',
...@@ -70,6 +73,7 @@ ...@@ -70,6 +73,7 @@
}, },
data(){ data(){
return { return {
currTitle:this.title,
visible:false, visible:false,
departList:[], departList:[],
departSelected:"", departSelected:"",
...@@ -100,7 +104,7 @@ ...@@ -100,7 +104,7 @@
this.departSelected = orgCode this.departSelected = orgCode
this.departList = departs this.departList = departs
if(this.currDepartName){ if(this.currDepartName){
this.title ="部门切换(当前部门 : "+this.currDepartName+")" this.currTitle ="部门切换(当前部门 : "+this.currDepartName+")"
} }
} }
...@@ -122,6 +126,10 @@ ...@@ -122,6 +126,10 @@
} }
putAction("/sys/selectDepart",obj).then(res=>{ putAction("/sys/selectDepart",obj).then(res=>{
if(res.success){ if(res.success){
const userInfo = res.result.userInfo;
Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000);
store.commit('SET_INFO', userInfo);
//console.log("---切换组织机构---userInfo-------",store.getters.userInfo.orgCode);
this.departClear() this.departClear()
} }
}) })
......
...@@ -78,6 +78,7 @@ ...@@ -78,6 +78,7 @@
<script> <script>
import { getAction,putAction } from '@/api/manage' import { getAction,putAction } from '@/api/manage'
import ShowAnnouncement from './ShowAnnouncement' import ShowAnnouncement from './ShowAnnouncement'
import store from '@/store/'
export default { export default {
name: "HeaderNotice", name: "HeaderNotice",
...@@ -89,7 +90,8 @@ ...@@ -89,7 +90,8 @@
loadding: false, loadding: false,
url:{ url:{
listCementByUser:"/sys/annountCement/listByUser", listCementByUser:"/sys/annountCement/listByUser",
editCementSend:"/system/sysAnnouncementSend/editByAnntIdAndUserId", editCementSend:"/sys/sysAnnouncementSend/editByAnntIdAndUserId",
queryById:"/sys/annountCement/queryById",
}, },
hovered: false, hovered: false,
announcement1:[], announcement1:[],
...@@ -98,6 +100,7 @@ ...@@ -98,6 +100,7 @@
msg2Count:"0", msg2Count:"0",
msg1Title:"通知(3)", msg1Title:"通知(3)",
msg2Title:"", msg2Title:"",
stopTimer:false,
} }
}, },
computed:{ computed:{
...@@ -105,15 +108,25 @@ ...@@ -105,15 +108,25 @@
return parseInt(this.msg1Count)+parseInt(this.msg2Count); return parseInt(this.msg1Count)+parseInt(this.msg2Count);
} }
}, },
created() { mounted() {
this.loadData(); this.loadData();
this.timer(); //this.timerFun();
this.initWebSocket();
},
destroyed: function () { // 离开页面生命周期函数
this.websocketclose();
}, },
methods: { methods: {
timer() { timerFun() {
return setInterval(()=>{ this.stopTimer = false;
let myTimer = setInterval(()=>{
// 停止定时器
if (this.stopTimer == true) {
clearInterval(myTimer);
return;
}
this.loadData() this.loadData()
},60000) },6000)
}, },
loadData (){ loadData (){
try { try {
...@@ -127,8 +140,14 @@ ...@@ -127,8 +140,14 @@
this.msg2Count = res.result.sysMsgTotal; this.msg2Count = res.result.sysMsgTotal;
this.msg2Title = "系统消息(" + res.result.sysMsgTotal + ")"; this.msg2Title = "系统消息(" + res.result.sysMsgTotal + ")";
} }
}).catch(error => {
console.log("系统消息通知异常",error);//这行打印permissionName is undefined
this.stopTimer = true;
console.log("清理timer");
}); });
} catch (err) { } catch (err) {
this.stopTimer = true;
console.log("通知异常",err);
} }
}, },
fetchNotice () { fetchNotice () {
...@@ -163,6 +182,74 @@ ...@@ -163,6 +182,74 @@
this.hovered = visible; this.hovered = visible;
}, },
initWebSocket: function () {
// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
var userId = store.getters.userInfo.id;
var url = window._CONFIG['domianURL'].replace("https://","wss://").replace("http://","ws://")+"/websocket/"+userId;
//console.log(url);
this.websock = new WebSocket(url);
this.websock.onopen = this.websocketonopen;
this.websock.onerror = this.websocketonerror;
this.websock.onmessage = this.websocketonmessage;
this.websock.onclose = this.websocketclose;
},
websocketonopen: function () {
console.log("WebSocket连接成功");
},
websocketonerror: function (e) {
console.log("WebSocket连接发生错误");
},
websocketonmessage: function (e) {
console.log("-----接收消息-------",e.data);
var data = eval("(" + e.data + ")"); //解析对象
this.loadData();
//if(data.cmd == "topic"){
//系统通知
this.openNotification(data);
//}else if(data.cmd == "user"){
//用户消息
// this.openNotification(data);
//}
},
websocketclose: function (e) {
console.log("connection closed (" + e.code + ")");
},
openNotification (data) {
var text = data.msgTxt;
const key = `open${Date.now()}`;
this.$notification.open({
message: '消息提醒',
placement:'bottomRight',
description: text,
key,
btn: (h)=>{
return h('a-button', {
props: {
type: 'primary',
size: 'small',
},
on: {
click: () => this.showDetail(key,data)
}
}, '查看详情')
},
});
},
showDetail(key,data){
this.$notification.close(key);
var id = data.msgId;
getAction(this.url.queryById,{id:id}).then((res) => {
if (res.success) {
var record = res.result;
this.showAnnouncement(record);
}
})
},
} }
} }
</script> </script>
......
<template> <template>
<div class="logo"> <div class="logo">
<router-link :to="{name:'dashboard'}"> <router-link :to="{name:'dashboard'}">
<img src="~@/assets/logo.svg" alt="logo">
<!-- update-begin- author:sunjianlei --- date:20190814 --- for: logo颜色根据主题颜色变化 -->
<img v-if="navTheme === 'dark'" src="~@/assets/logo-white.png" alt="logo">
<img v-else src="~@/assets/logo.svg" alt="logo">
<!-- update-begin- author:sunjianlei --- date:20190814 --- for: logo颜色根据主题颜色变化 -->
<h1 v-if="showTitle">{{ title }}</h1> <h1 v-if="showTitle">{{ title }}</h1>
</router-link> </router-link>
</div> </div>
</template> </template>
<script> <script>
import { mixin } from '@/utils/mixin.js'
export default { export default {
name: 'Logo', name: 'Logo',
mixins: [mixin],
props: { props: {
title: { title: {
type: String, type: String,
......
<template> <template>
<a-modal <a-modal
width="60%" class="announcementCustomModal"
:width="modelStyle.width"
:visible="visible" :visible="visible"
:bodyStyle ="bodyStyle" :bodyStyle ="bodyStyle"
@cancel="handleCancel" @cancel="handleCancel"
destroyOnClose destroyOnClose
:footer="null"> :footer="null">
<template slot="title">
<a-card style="width: 100%;height: 100%" class="daily-article" :loading="loading"> <a-button icon="fullscreen" class="custom-btn" @click="handleClickToggleFullScreen"/>
</template>
<a-card class="daily-article" :loading="loading">
<a-card-meta <a-card-meta
:title="record.titile" :title="record.titile"
:description="'发布人:'+record.sender + ' 发布时间: ' + record.sendTime"/> :description="'发布人:'+record.sender + ' 发布时间: ' + record.sendTime">
</a-card-meta>
<a-divider /> <a-divider />
<span v-html="record.msgContent" class="article-content"></span> <span v-html="record.msgContent" class="article-content"></span>
</a-card> </a-card>
</a-modal> </a-modal>
</template> </template>
...@@ -40,8 +43,14 @@ ...@@ -40,8 +43,14 @@
bodyStyle:{ bodyStyle:{
padding: "0", padding: "0",
height:(window.innerHeight*0.8)+"px", height:(window.innerHeight*0.8)+"px",
"overflow-y":"auto" "overflow-y":"auto",
}, },
modelStyle:{
width: '60%',
style: { top: '20px' },
fullScreen: false
}
} }
}, },
created () { created () {
...@@ -54,33 +63,68 @@ ...@@ -54,33 +63,68 @@
handleCancel () { handleCancel () {
this.visible = false; this.visible = false;
}, },
/** 切换全屏显示 */
handleClickToggleFullScreen() {
let mode = !this.modelStyle.fullScreen
if (mode) {
this.modelStyle.width = '100%'
this.modelStyle.style.top = '20px'
} else {
this.modelStyle.width = '60%'
this.modelStyle.style.top = '50px'
}
this.modelStyle.fullScreen = mode
}
} }
} }
</script> </script>
<style lang="less">
.announcementCustomModal{
.ant-modal-header {
border: none;
display: inline-block;
position: absolute;
z-index: 1;
right: 56px;
padding: 0;
.ant-modal-title{
.custom-btn{
width: 56px;
height: 56px;
border: none;
box-shadow: none;
}
}
}
.daily-article{
border-bottom: 0;
}
}
</style>
<style scoped lang="less"> <style scoped lang="less">
.daily-article { .daily-article {
.article-button { .article-button {
font-size: 1.2rem !important; font-size: 1.2rem !important;
} }
.ant-card-body { .ant-card-body {
padding: 18px !important; padding: 18px !important;
} }
.ant-card-head { .ant-card-head {
padding: 0 1rem; padding: 0 1rem;
} }
.ant-card-meta { .ant-card-meta {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.article-content { .article-content {
p { p {
word-wrap: break-word; word-wrap: break-word;
word-break: break-all; word-break: break-all;
text-overflow: initial; text-overflow: initial;
white-space: normal; white-space: normal;
font-size: .9rem !important; font-size: .9rem !important;
margin-bottom: .8rem; margin-bottom: .8rem;
} }
} }
} }
</style> </style>
\ No newline at end of file
...@@ -24,11 +24,15 @@ ...@@ -24,11 +24,15 @@
<span>账户设置</span> <span>账户设置</span>
</router-link> </router-link>
</a-menu-item> </a-menu-item>
<a-menu-item key="2" @click="updatePassword"> <a-menu-item key="3" @click="systemSetting">
<a-icon type="tool"/>
<span>系统设置</span>
</a-menu-item>
<a-menu-item key="4" @click="updatePassword">
<a-icon type="setting"/> <a-icon type="setting"/>
<span>密码修改</span> <span>密码修改</span>
</a-menu-item> </a-menu-item>
<a-menu-item key="3" @click="updateCurrentDepart"> <a-menu-item key="5" @click="updateCurrentDepart">
<a-icon type="cluster"/> <a-icon type="cluster"/>
<span>切换部门</span> <span>切换部门</span>
</a-menu-item> </a-menu-item>
...@@ -53,12 +57,14 @@ ...@@ -53,12 +57,14 @@
</span> </span>
<user-password ref="userPassword"></user-password> <user-password ref="userPassword"></user-password>
<depart-select ref="departSelect" :closable="true" title="部门切换"></depart-select> <depart-select ref="departSelect" :closable="true" title="部门切换"></depart-select>
<setting-drawer ref="settingDrawer" :closable="true" title="系统设置"></setting-drawer>
</div> </div>
</template> </template>
<script> <script>
import HeaderNotice from './HeaderNotice' import HeaderNotice from './HeaderNotice'
import UserPassword from './UserPassword' import UserPassword from './UserPassword'
import SettingDrawer from "@/components/setting/SettingDrawer";
import DepartSelect from './DepartSelect' import DepartSelect from './DepartSelect'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import { mixinDevice } from '@/utils/mixin.js' import { mixinDevice } from '@/utils/mixin.js'
...@@ -69,7 +75,8 @@ ...@@ -69,7 +75,8 @@
components: { components: {
HeaderNotice, HeaderNotice,
UserPassword, UserPassword,
DepartSelect DepartSelect,
SettingDrawer
}, },
props: { props: {
theme: { theme: {
...@@ -112,6 +119,9 @@ ...@@ -112,6 +119,9 @@
}, },
updateCurrentDepart(){ updateCurrentDepart(){
this.$refs.departSelect.show() this.$refs.departSelect.show()
},
systemSetting(){
this.$refs.settingDrawer.showDrawer()
} }
} }
} }
......
...@@ -323,30 +323,6 @@ export const constantRouterMap = [ ...@@ -323,30 +323,6 @@ export const constantRouterMap = [
] ]
}, },
// {
// path: '/',
// name: 'index',
// component: TabLayout,
// meta: {title: '首页'},
// redirect: '/dashboard/workplace',
// children: [
// {
// path: '/online',
// name: 'online',
// redirect: '/online',
// component: RouteView,
// meta: {title: '在线开发', icon: 'dashboard', permission: ['dashboard']},
// children: [
// {
// path: '/online/auto/:code',
// name: 'report',
// component: () => import('@/views/modules/online/cgreport/OnlCgreportAutoList')
// },
// ]
// },
// ]
// },
{ {
path: '/test', path: '/test',
component: BlankLayout, component: BlankLayout,
......
...@@ -18,6 +18,9 @@ import VueApexCharts from 'vue-apexcharts' ...@@ -18,6 +18,9 @@ import VueApexCharts from 'vue-apexcharts'
import preview from 'vue-photo-preview' import preview from 'vue-photo-preview'
import 'vue-photo-preview/dist/skin.css' import 'vue-photo-preview/dist/skin.css'
import "@jeecg/antd-onine"
import '@jeecg/antd-onine/dist/OnlineForm.css'
import { import {
ACCESS_TOKEN, ACCESS_TOKEN,
......
...@@ -3,6 +3,7 @@ import { login, logout, phoneLogin } from "@/api/login" ...@@ -3,6 +3,7 @@ import { login, logout, phoneLogin } from "@/api/login"
import { ACCESS_TOKEN, USER_NAME,USER_INFO,USER_AUTH,SYS_BUTTON_AUTH } from "@/store/mutation-types" import { ACCESS_TOKEN, USER_NAME,USER_INFO,USER_AUTH,SYS_BUTTON_AUTH } from "@/store/mutation-types"
import { welcome } from "@/utils/util" import { welcome } from "@/utils/util"
import { queryPermissionsByUser } from '@/api/api' import { queryPermissionsByUser } from '@/api/api'
import { getAction } from '@/api/manage'
const user = { const user = {
state: { state: {
...@@ -36,6 +37,30 @@ const user = { ...@@ -36,6 +37,30 @@ const user = {
}, },
actions: { actions: {
// CAS验证登录
ValidateLogin({ commit }, userInfo) {
return new Promise((resolve, reject) => {
getAction("/cas/client/validateLogin",userInfo).then(response => {
console.log("----cas 登录--------",response);
if(response.success){
const result = response.result
const userInfo = result.userInfo
Vue.ls.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000)
Vue.ls.set(USER_NAME, userInfo.username, 7 * 24 * 60 * 60 * 1000)
Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000)
commit('SET_TOKEN', result.token)
commit('SET_INFO', userInfo)
commit('SET_NAME', { username: userInfo.username,realname: userInfo.realname, welcome: welcome() })
commit('SET_AVATAR', userInfo.avatar)
resolve(response)
}else{
resolve(response)
}
}).catch(error => {
reject(error)
})
})
},
// 登录 // 登录
Login({ commit }, userInfo) { Login({ commit }, userInfo) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
...@@ -115,6 +140,9 @@ const user = { ...@@ -115,6 +140,9 @@ const user = {
Vue.ls.remove(ACCESS_TOKEN) Vue.ls.remove(ACCESS_TOKEN)
//console.log('logoutToken: '+ logoutToken) //console.log('logoutToken: '+ logoutToken)
logout(logoutToken).then(() => { logout(logoutToken).then(() => {
//var sevice = "http://"+window.location.host+"/";
//var serviceUrl = encodeURIComponent(sevice);
//window.location.href = window._CONFIG['casPrefixUrl']+"/logout?service="+serviceUrl;
resolve() resolve()
}).catch(() => { }).catch(() => {
resolve() resolve()
......
...@@ -78,9 +78,11 @@ service.interceptors.request.use(config => { ...@@ -78,9 +78,11 @@ service.interceptors.request.use(config => {
config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改 config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
} }
if(config.method=='get'){ if(config.method=='get'){
config.params = { if(config.url.indexOf("sys/dict/getDictItems")<0){
_t: Date.parse(new Date())/1000, config.params = {
...config.params _t: Date.parse(new Date())/1000,
...config.params
}
} }
} }
return config return config
......
...@@ -232,4 +232,23 @@ export function showDealBtn(bpmStatus){ ...@@ -232,4 +232,23 @@ export function showDealBtn(bpmStatus){
return true; return true;
} }
return false; return false;
}
/**
* 增强CSS,可以在页面上输出全局css
* @param css 要增强的css
* @param id style标签的id,可以用来清除旧样式
*/
export function cssExpand(css, id) {
let style = document.createElement('style')
style.type = "text/css"
style.innerHTML = `@charset "UTF-8"; ${css}`
// 清除旧样式
if (id) {
let $style = document.getElementById(id)
if ($style != null) $style.outerHTML = ''
style.id = id
}
// 应用新样式
document.head.appendChild(style)
} }
\ No newline at end of file
...@@ -209,10 +209,10 @@ ...@@ -209,10 +209,10 @@
}) })
getVisitInfo().then(res=>{ getVisitInfo().then(res=>{
if(res.success){ if(res.success){
console.log("aaaaaa",res.result) console.log("aaaaaa",res.result)
this.visitInfo = res.result; this.visitInfo = res.result;
} }
}) })
}, },
} }
} }
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :span="12"> <a-col :span="12">
<a-form-item label="选择部门"> <a-form-item label="选择部门">
<j-select-depart v-model="departId"></j-select-depart> <j-select-depart v-model="departId" :multi="true"></j-select-depart>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="12">选中的部门ID(v-model):{{ departId }}</a-col> <a-col :span="12">选中的部门ID(v-model):{{ departId }}</a-col>
...@@ -217,10 +217,8 @@ ...@@ -217,10 +217,8 @@
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :span="12"> <a-col :span="12">
<a-form-item label="cron表达式"> <a-form-item label="cron表达式">
<a-input @click="openModal" placeholder="corn表达式" v-model="cron.label" readOnly > <j-cron ref="innerVueCron" v-decorator="['cronExpression', {'initialValue':'0/1 * * * * ?'}]" @change="setCorn"></j-cron>
<a-icon slot="prefix" type="schedule" title="corn控件"/> <!-- <j-cron ref="innerVueCron" v-model="cron" @change="setCorn"></j-cron>-->
</a-input>
<VueCron ref="innerVueCron" :data="cron" @change="changeCron" ></VueCron>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
...@@ -245,7 +243,7 @@ ...@@ -245,7 +243,7 @@
import JSlider from '@/components/jeecg/JSlider' import JSlider from '@/components/jeecg/JSlider'
import JSelectMultiple from '@/components/jeecg/JSelectMultiple' import JSelectMultiple from '@/components/jeecg/JSelectMultiple'
import JTreeDict from "../../components/jeecg/JTreeDict.vue"; import JTreeDict from "../../components/jeecg/JTreeDict.vue";
import VueCron from "./modules/VueCronModal.vue"; import JCron from "@/components/jeecg/JCron.vue";
export default { export default {
name: 'SelectDemo', name: 'SelectDemo',
components: { components: {
...@@ -257,7 +255,7 @@ ...@@ -257,7 +255,7 @@
JCheckbox, JCheckbox,
JCodeEditor, JCodeEditor,
JDate, JEditor, JEllipsis, JGraphicCode, JSlider, JSelectMultiple, JDate, JEditor, JEllipsis, JGraphicCode, JSlider, JSelectMultiple,
VueCron JCron
}, },
data() { data() {
return { return {
...@@ -314,10 +312,7 @@ sayHi('hello, world!')` ...@@ -314,10 +312,7 @@ sayHi('hello, world!')`
style: { top: '20px' }, style: { top: '20px' },
fullScreen: true fullScreen: true
}, },
cron: { cron: '',
label: '',
value: {}
}
} }
}, },
computed: { computed: {
...@@ -372,14 +367,12 @@ sayHi('hello, world!')` ...@@ -372,14 +367,12 @@ sayHi('hello, world!')`
} }
this.modal.fullScreen = mode this.modal.fullScreen = mode
}, },
openModal(){ setCorn(data){
this.$refs.innerVueCron.show()
},
changeCron(val){
this.cron=val;
console.log(val);
}
this.$nextTick(() => {
this.form.cronExpression = data;
})
}
} }
} }
</script> </script>
......
<template>
<a-card :bordered="false">
<a-table
rowKey="id"
bordered
:columns="columns"
:dataSource="dataSource"
:pagination="false"
>
</a-table>
</a-card>
</template>
<script>
export default {
name: 'TableTotal',
data() {
return {
columns: [
{
title: '#',
width: '180px',
align: 'center',
dataIndex: 'rowIndex',
customRender: function (text, r, index) {
return (text !== '合计') ? (parseInt(index) + 1) : text
}
},
{
title: '姓名',
dataIndex: 'name',
},
{
title: '贡献点',
dataIndex: 'point',
},
{
title: '等级',
dataIndex: 'level',
},
{
title: '更新时间',
dataIndex: 'updateTime',
},
],
dataSource: [
{ name: '张三', point: 23, level: 3, updateTime: '2019-8-14' },
{ name: '小王', point: 6, level: 1, updateTime: '2019-8-13' },
{ name: '李四', point: 53, level: 8, updateTime: '2019-8-12' },
{ name: '小红', point: 44, level: 5, updateTime: '2019-8-11' },
{ name: '王五', point: 97, level: 10, updateTime: '2019-8-10' },
{ name: '小明', point: 33, level: 2, updateTime: '2019-8-10' },
]
}
},
mounted() {
this.tableAddTotalRow(this.columns, this.dataSource)
},
methods: {
/** 表格增加合计行 */
tableAddTotalRow(columns, dataSource) {
let numKey = 'rowIndex'
let totalRow = { [numKey]: '合计' }
columns.forEach(column => {
let { key, dataIndex } = column
if (![key, dataIndex].includes(numKey)) {
let total = 0
dataSource.forEach(data => {
total += /^\d+\.?\d?$/.test(data[dataIndex]) ? Number.parseInt(data[dataIndex]) : Number.NaN
console.log(data[dataIndex], ':', (/^\d+\.?\d?$/.test(data[dataIndex]) ? Number.parseInt(data[dataIndex]) : Number.NaN))
})
if (Number.isNaN(total)) {
total = '-'
}
totalRow[dataIndex] = total
}
})
dataSource.push(totalRow)
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<div>
<a-button @click="handleTableCheck" type="primary">表单验证</a-button>
<span style="padding-left:8px;"></span>
<a-tooltip placement="top" title="获取值,忽略表单验证" :autoAdjustOverflow="true">
<a-button @click="handleTableGet" type="primary">获取值</a-button>
</a-tooltip>
<span style="padding-left:8px;"></span>
<a-tooltip placement="top" title="模拟加载1000条数据" :autoAdjustOverflow="true">
<a-button @click="handleTableSet" type="primary">设置值</a-button>
</a-tooltip>
<j-editable-table
ref="editableTable"
:loading="loading"
:columns="columns"
:dataSource="dataSource"
:rowNumber="true"
:rowSelection="true"
:actionButton="true"
:dragSort="true"
style="margin-top: 8px;"
@selectRowChange="handleSelectRowChange">
<template v-slot:action="props">
<a @click="handleDelete(props)">{{ props.text }}</a>
</template>
</j-editable-table>
</div>
</template>
<script>
import moment from 'moment'
import { FormTypes } from '@/utils/JEditableTableUtil'
import { randomUUID, randomNumber } from '@/utils/util'
import JEditableTable from '@/components/jeecg/JEditableTable'
export default {
name: 'DefaultTable',
components: { JEditableTable },
data() {
return {
loading: false,
columns: [
{
title: '字段名称',
key: 'dbFieldName',
// width: '19%',
width: '300px',
type: FormTypes.input,
defaultValue: '',
placeholder: '请输入${title}',
validateRules: [
{
required: true, // 必填
message: '请输入${title}' // 显示的文本
},
{
pattern: /^[a-z|A-Z][a-z|A-Z\d_-]{0,}$/, // 正则
message: '${title}必须以字母开头,可包含数字、下划线、横杠'
}
]
},
{
title: '文件域',
key: 'upload',
type: FormTypes.upload,
// width: '19%',
width: '300px',
placeholder: '点击上传',
token: true,
responseName: 'message',
action: window._CONFIG['domianURL'] + '/sys/common/upload'
},
{
title: '字段类型',
key: 'dbFieldType',
// width: '18%',
width: '300px',
type: FormTypes.select,
options: [ // 下拉选项
{ title: 'String', value: 'string' },
{ title: 'Integer', value: 'int' },
{ title: 'Double', value: 'double' },
{ title: 'Boolean', value: 'boolean' }
],
allowInput: true,
defaultValue: '',
placeholder: '请选择${title}',
validateRules: [{ required: true, message: '请选择${title}' }]
},
{
title: '性别(字典)',
key: 'sex_dict',
width: '300px',
type: FormTypes.select,
options: [],
dictCode: 'sex',
placeholder: '请选择${title}',
validateRules: [{ required: true, message: '请选择${title}' }]
},
{
title: '多选测试',
key: 'multipleSelect',
// width: '18%',
width: '300px',
type: FormTypes.select,
props: { 'mode': 'multiple' }, // 支持多选
options: [
{ title: 'String', value: 'string' },
{ title: 'Integer', value: 'int' },
{ title: 'Double', value: 'double' },
{ title: 'Boolean', value: 'boolean' }
],
defaultValue: ['int', 'boolean'], // 多个默认项
// defaultValue: 'string,double,int', // 也可使用这种方式
placeholder: '这里可以多选',
validateRules: [{ required: true, message: '请选择${title}' }]
},
{
title: '字段长度',
key: 'dbLength',
// width: '8%',
width: '100px',
type: FormTypes.inputNumber,
defaultValue: 32,
placeholder: '${title}',
validateRules: [{ required: true, message: '请输入${title}' }]
},
{
title: '日期',
key: 'datetime',
// width: '22%',
width: '320px',
type: FormTypes.datetime,
defaultValue: '2019-4-30 14:52:22',
placeholder: '请选择${title}',
validateRules: [{ required: true, message: '请选择${title}' }]
},
{
title: '可以为空',
key: 'isNull',
// width: '8%',
width: '100px',
type: FormTypes.checkbox,
customValue: ['Y', 'N'], // true ,false
defaultChecked: false
},
{
title: '操作',
key: 'action',
// width: '8%',
width: '100px',
type: FormTypes.slot,
slotName: 'action',
defaultValue: '删除'
}
],
dataSource: [],
selectedRowIds: []
}
},
mounted() {
this.randomData(23, false)
},
methods: {
/** 表单验证 */
handleTableCheck() {
this.$refs.editableTable.getValues((error) => {
if (error === 0) {
this.$message.success('验证通过')
} else {
this.$message.error('验证未通过')
}
})
},
/** 获取值,忽略表单验证 */
handleTableGet() {
this.$refs.editableTable.getValues((error, values) => {
console.log('values:', values)
}, false)
console.log('deleteIds:', this.$refs.editableTable.getDeleteIds())
this.$message.info('获取值成功,请看控制台输出')
},
/** 模拟加载1000条数据 */
handleTableSet() {
this.randomData(1000, true)
},
handleSelectRowChange(selectedRowIds) {
this.selectedRowIds = selectedRowIds
},
/* 随机生成数据 */
randomData(size, loading = false) {
if (loading) {
this.loading = true
}
let randomDatetime = () => {
let time = parseInt(randomNumber(1000, 9999999999999))
return moment(new Date(time)).format('YYYY-MM-DD HH:mm:ss')
}
let begin = Date.now()
let values = []
for (let i = 0; i < size; i++) {
values.push({
id: randomUUID(),
dbFieldName: `name_${i + 1}`,
// dbFieldTxt: randomString(10),
multipleSelect: ['string', ['int', 'double', 'boolean'][randomNumber(0, 2)]],
dbFieldType: ['string', 'int', 'double', 'boolean'][randomNumber(0, 3)],
dbLength: randomNumber(0, 233),
datetime: randomDatetime(),
isNull: ['Y', 'N'][randomNumber(0, 1)]
})
}
this.dataSource = values
let end = Date.now()
let diff = end - begin
if (loading && diff < size) {
setTimeout(() => {
this.loading = false
}, size - diff)
}
},
handleDelete(props) {
let { rowId, target } = props
target.removeRows(rowId)
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<j-editable-table
:columns="columns"
:dataSource="dataSource"
:rowNumber="true"
:rowSelection="true"
:maxHeight="400"
:disabled="true"
/>
</template>
<script>
import { FormTypes } from '@/utils/JEditableTableUtil'
import JEditableTable from '@/components/jeecg/JEditableTable'
export default {
name: 'ReadOnlyTable',
components: { JEditableTable },
data() {
return {
columns: [
{
title: '输入框',
key: 'input',
type: FormTypes.input,
placeholder: '清输入'
},
{
title: '下拉框',
key: 'select',
type: FormTypes.select,
options: [
{ title: 'String', value: 'string' },
{ title: 'Integer', value: 'int' },
{ title: 'Double', value: 'double' },
{ title: 'Boolean', value: 'boolean' }
],
placeholder: '请选择'
},
{
title: '多选框',
key: 'checkbox',
type: FormTypes.checkbox,
customValue: [true, false]
},
{
title: '日期',
key: 'datetime',
type: FormTypes.datetime
}
],
dataSource: [
{ input: 'hello', select: 'int', checkbox: true, datetime: '2019-6-17 14:50:48' },
{ input: 'world', select: 'string', checkbox: false, datetime: '2019-6-16 14:50:48' },
{ input: 'one', select: 'double', checkbox: true, datetime: '2019-6-17 15:50:48' },
{ input: 'two', select: 'boolean', checkbox: false, datetime: '2019-6-14 14:50:48' },
{ input: 'three', select: '', checkbox: false, datetime: '2019-6-13 14:50:48' }
]
}
},
mounted() {
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<j-editable-table
:columns="columns"
:dataSource="dataSource"
:rowNumber="true"
:actionButton="true"
:rowSelection="true"
:maxHeight="400"
@valueChange="handleValueChange"
/>
</template>
<script>
import { FormTypes } from '@/utils/JEditableTableUtil'
import JEditableTable from '@/components/jeecg/JEditableTable'
export default {
name: 'ThreeLinkage',
components: { JEditableTable },
data() {
return {
columns: [
{
title: '省/直辖市/自治区',
key: 's1',
type: FormTypes.select,
width: '240px',
options: [],
placeholder: '请选择${title}'
},
{
title: '市',
key: 's2',
type: FormTypes.select,
width: '240px',
options: [],
placeholder: '请选择${title}'
},
{
title: '县/区',
key: 's3',
type: FormTypes.select,
width: '240px',
options: [],
placeholder: '请选择${title}'
}
],
dataSource: [],
mockData: [
{ label: '北京市', value: '110000', parent: null },
{ label: '天津市', value: '120000', parent: null },
{ label: '河北省', value: '130000', parent: null },
{ label: '上海市', value: '310000', parent: null },
{ label: '北京市', value: '110100', parent: '110000' },
{ label: '天津市市', value: '120100', parent: '120000' },
{ label: '石家庄市', value: '130100', parent: '130000' },
{ label: '唐山市', value: '130200', parent: '130000' },
{ label: '秦皇岛市', value: '130300', parent: '130000' },
{ label: '上海市', value: '310100', parent: '310000' },
{ label: '东城区', value: '110101', parent: '110100' },
{ label: '西城区', value: '110102', parent: '110100' },
{ label: '朝阳区', value: '110105', parent: '110100' },
{ label: '和平区', value: '120101', parent: '120000' },
{ label: '河东区', value: '120102', parent: '120000' },
{ label: '河西区', value: '120103', parent: '120000' },
{ label: '黄浦区', value: '310101', parent: '310100' },
{ label: '徐汇区', value: '310104', parent: '310100' },
{ label: '长宁区', value: '310105', parent: '310100' },
{ label: '长安区', value: '130102', parent: '130100' },
{ label: '桥西区', value: '130104', parent: '130100' },
{ label: '新华区', value: '130105', parent: '130100' },
{ label: '路南区', value: '130202', parent: '130200' },
{ label: '路北区', value: '130203', parent: '130200' },
{ label: '古冶区', value: '130204', parent: '130200' },
{ label: '海港区', value: '130302', parent: '130300' },
{ label: '山海关区', value: '130303', parent: '130300' },
{ label: '北戴河区', value: '130304', parent: '130300' },
]
}
},
mounted() {
// 初始化数据
this.columns[0].options = this.request(null)
},
methods: {
request(parentId) {
return this.mockData.filter(i => i.parent === parentId)
},
/** 当选项被改变时,联动其他组件 */
handleValueChange(event) {
const { type, row, column, value, target } = event
if (type === FormTypes.select) {
// 第一列
if (column.key === 's1') {
// 设置第二列的 options
this.columns[1].options = this.request(value)
// 清空后两列的数据
target.setValues([{
rowKey: row.id,
values: { s2: '', s3: '' }
}])
this.columns[2].options = []
} else
// 第二列
if (column.key === 's2') {
this.columns[2].options = this.request(value)
target.setValues([{
rowKey: row.id,
values: { s3: '' }
}])
}
}
}
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
...@@ -486,7 +486,7 @@ ...@@ -486,7 +486,7 @@
days = this.result.day.cronLastSpecificDomDay + 'L'; days = this.result.day.cronLastSpecificDomDay + 'L';
break; break;
case '9': case '9':
days = 'L-' + this.day.cronDaysBeforeEomMinus; days = 'L-' + this.result.day.cronDaysBeforeEomMinus;
break; break;
case '10': case '10':
days = this.result.day.cronDaysNearestWeekday+"W"; days = this.result.day.cronDaysNearestWeekday+"W";
......
/**
* 同步列表,可以同步新增、修改、删除
* @author sunjianlei
* */
export function syncAllTable(vm, table1) {
vm.$refs.editableTable.resetScrollTop()
let deleteIds = table1.$refs.editableTable.getDeleteIds()
let table1Value
table1.$refs.editableTable.getValuesPromise(false).then((values) => {
table1Value = values
return vm.$refs.editableTable.getValuesPromise(false)
}).then((values) => {
table1Value.forEach(value => {
let flag = false
values.forEach((thisValue) => {
if (value.id === thisValue.id) {
// 判断是否修改了值
let dbFieldName = thisValue['dbFieldName']
let dbFieldTxt = thisValue['dbFieldTxt']
// return
if (value.dbFieldName !== dbFieldName
|| value.dbFieldTxt !== dbFieldTxt) {
// 修改了
vm.$refs.editableTable.setValues([{
rowKey: thisValue.id,
values: {
dbFieldName: value.dbFieldName,
dbFieldTxt: value.dbFieldTxt
}
}])
}
flag = true
} else {
// id不匹配则有可能是新增也有可能是删除了的
// 遍历传进来的 deleteIds 进行对比
deleteIds.forEach(delId => {
// 对比成功,则删除该条数据
if (delId === thisValue.id) {
vm.$refs.editableTable.removeRows(vm.$refs.editableTable.caseId + delId)
flag = true
}
})
}
})
// return
// 判断是否操作了该条数据,若没有操作则代表要执行新增操作
if (!flag) {
let record = Object.assign({}, value)
vm.columns.forEach(column => {
if (
column.dataIndex !== 'dbFieldName' &&
column.dataIndex !== 'dbFieldTxt'
) {
record[column.dataIndex] = column.defaultValue
}
})
vm.$refs.editableTable.push(record)
}
})
})
}
/**
* 将数据分类并Set进dataSource
* @author sunjianlei
**/
export function setDataSource(vm, queryData) {
let dataSource = []
// 遍历查询出来的数据
queryData.forEach(value => {
let data = { id: value['id'] }
vm.columns.forEach(column => {
let key = column.key
if (key) {
data[key] = value[key]
// 由于多选下拉框返回的是一个数组,所以需要改成 [1,2,3] 数组的形式,否则组件不识别
// if (key === 'indexField') {
// data[key] = value[key].split(',')
// }
}
})
dataSource.push(data)
})
vm.dataSource = dataSource
}
/** 获取主表的初始化数据 */
export function getMasterTableInitialData() {
return [
{
dbFieldName: 'id',
dbFieldTxt: '主键',
dbLength: 36,
dbPointLength: 0,
dbDefaultVal: '',
dbType: 'string',
dbIsKey: '1',
dbIsNull: '0',
// table2
isShowForm: '0',
isShowList: '0',
fieldShowType: 'text',
fieldLength: '120',
queryMode: 'single',
orderNum: 1
},
{
dbFieldName: 'create_by',
dbFieldTxt: '创建人',
dbLength: 50,
dbPointLength: 0,
dbDefaultVal: '',
dbType: 'string',
dbIsKey: '0',
dbIsNull: '1',
// table2
isShowForm: '0',
isShowList: '0',
fieldShowType: 'text',
fieldLength: '120',
queryMode: 'single',
orderNum: 2
},
{
dbFieldName: 'create_time',
dbFieldTxt: '创建日期',
dbLength: 20,
dbPointLength: 0,
dbDefaultVal: '',
dbType: 'Date',
dbIsKey: '0',
dbIsNull: '1',
// table2
isShowForm: '0',
isShowList: '0',
fieldShowType: 'datetime',
fieldLength: '120',
queryMode: 'single',
orderNum: 3
},
{
dbFieldName: 'update_by',
dbFieldTxt: '更新人',
dbLength: 50,
dbPointLength: 0,
dbDefaultVal: '',
dbType: 'string',
dbIsKey: '0',
dbIsNull: '1',
// table2
isShowForm: '0',
isShowList: '0',
fieldShowType: 'text',
fieldLength: '120',
queryMode: 'single',
orderNum: 4
},
{
dbFieldName: 'update_time',
dbFieldTxt: '更新日期',
dbLength: 20,
dbPointLength: 0,
dbDefaultVal: '',
dbType: 'Date',
dbIsKey: '0',
dbIsNull: '1',
// table2
isShowForm: '0',
isShowList: '0',
fieldShowType: 'datetime',
fieldLength: '120',
queryMode: 'single',
orderNum: 5
},{
dbFieldName: 'sys_org_code',
dbFieldTxt: '所属部门',
dbLength: 64,
dbPointLength: 0,
dbDefaultVal: '',
dbType: 'string',
dbIsKey: '0',
dbIsNull: '1',
// table2
isShowForm: '0',
isShowList: '0',
fieldShowType: 'text',
fieldLength: '120',
queryMode: 'single',
orderNum: 6
}
// {
// dbFieldName: 'sys_org_code',
// dbFieldTxt: '所属部门',
// dbLength: 50,
// dbPointLength: 0,
// dbDefaultVal: '',
// dbType: 'string',
// dbIsKey: false,
// dbIsNull: true
// }, {
// dbFieldName: 'sys_company_code',
// dbFieldTxt: '所属公司',
// dbLength: 50,
// dbPointLength: 0,
// dbDefaultVal: '',
// dbType: 'string',
// dbIsKey: false,
// dbIsNull: true
// }, {
// dbFieldName: 'bpm_status',
// dbFieldTxt: '流程状态',
// dbLength: 32,
// dbPointLength: 0,
// dbDefaultVal: '',
// dbType: 'string',
// dbIsKey: false,
// dbIsNull: true
// }
]
}
/** 获取树的初始化数据 */
export function getTreeNeedFields() {
return [{
dbFieldName: 'pid',
dbFieldTxt: '父级节点',
dbLength: 32,
dbPointLength: 0,
dbDefaultVal: '',
dbType: 'string',
dbIsKey: '0',
dbIsNull: '1',
// table2
isShowForm: '1',
isShowList: '0',
fieldShowType: 'text',
fieldLength: '120',
queryMode: 'single',
orderNum: 7
},{
dbFieldName: 'has_child',
dbFieldTxt: '是否有子节点',
dbLength: 3,
dbPointLength: 0,
dbDefaultVal: '',
dbType: 'string',
dbIsKey: '0',
dbIsNull: '1',
// table2
isShowForm: '0',
isShowList: '0',
fieldShowType: 'list',
fieldLength: '120',
queryMode: 'single',
orderNum: 8,
// table3
dictField:"yn"
}]
}
\ No newline at end of file
...@@ -205,11 +205,11 @@ ...@@ -205,11 +205,11 @@
mobile: {rules: [{validator: this.validateMobile}]} mobile: {rules: [{validator: this.validateMobile}]}
}, },
url: { url: {
delete: '/sysdepart/sysDepart/delete', delete: '/sys/sysDepart/delete',
edit: '/sysdepart/sysDepart/edit', edit: '/sys/sysDepart/edit',
deleteBatch: '/sysdepart/sysDepart/deleteBatch', deleteBatch: '/sys/sysDepart/deleteBatch',
exportXlsUrl: "sysdepart/sysDepart/exportXls", exportXlsUrl: "sys/sysDepart/exportXls",
importExcelUrl: "sysdepart/sysDepart/importExcel", importExcelUrl: "sys/sysDepart/importExcel",
}, },
} }
}, },
...@@ -292,7 +292,7 @@ ...@@ -292,7 +292,7 @@
var that = this var that = this
this.$confirm({ this.$confirm({
title: '确认删除', title: '确认删除',
content: '确定要删除所选中的 ' + this.checkedKeys.length + ' 条数据?', content: '确定要删除所选中的 ' + this.checkedKeys.length + ' 条数据,以及子节点数据吗?',
onOk: function () { onOk: function () {
deleteAction(that.url.deleteBatch, {ids: ids}).then((res) => { deleteAction(that.url.deleteBatch, {ids: ids}).then((res) => {
if (res.success) { if (res.success) {
......
...@@ -148,9 +148,9 @@ ...@@ -148,9 +148,9 @@
selectedRowKeys: [], selectedRowKeys: [],
selectedRows: [], selectedRows: [],
url: { url: {
list: "/sysdepart/sysDepart/list", list: "/sys/sysDepart/list",
delete: "/sysdepart/sysDepart/delete", delete: "/sys/sysDepart/delete",
deleteBatch: "/sysdepart/sysDepart/deleteBatch", deleteBatch: "/sys/sysDepart/deleteBatch",
}, },
} }
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :md="8" :sm="10"> <a-col :md="6" :sm="10">
<a-form-item label="创建时间" :labelCol="labelCol" :wrapperCol="wrapperCol"> <a-form-item label="创建时间" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker <a-range-picker
style="width: 210px" style="width: 210px"
...@@ -31,14 +31,19 @@ ...@@ -31,14 +31,19 @@
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :md="5" :sm="8" v-if="tabKey === '2'">
<a-col :md="8" :sm="10" > <a-form-item label="操作类型" style="left: 10px">
<span style="float: right;" class="table-page-search-submitButtons"> <j-dict-select-tag v-model="queryParam.operateType" placeholder="请选择操作类型" dictCode="operate_type"/>
<a-button type="primary" style="left: -35px" @click="searchQuery" icon="search">查询</a-button> </a-form-item>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px;left: -35px">重置</a-button>
</span>
</a-col> </a-col>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24" >
<a-button type="primary" style="left: 10px" @click="searchQuery" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px;left: 10px">重置</a-button>
</a-col>
</span>
</a-row> </a-row>
</a-form> </a-form>
</div> </div>
...@@ -58,7 +63,10 @@ ...@@ -58,7 +63,10 @@
<div style="margin-bottom: 5px"><a-badge status="success" style="vertical-align: middle;"/><span style="vertical-align: middle;">请求方法:{{ record.method }}</span></div> <div style="margin-bottom: 5px"><a-badge status="success" style="vertical-align: middle;"/><span style="vertical-align: middle;">请求方法:{{ record.method }}</span></div>
<div><a-badge status="processing" style="vertical-align: middle;"/><span style="vertical-align: middle;">请求参数:{{ record.requestParam }}</span></div> <div><a-badge status="processing" style="vertical-align: middle;"/><span style="vertical-align: middle;">请求参数:{{ record.requestParam }}</span></div>
</div> </div>
<!-- 字符串超长截取省略号显示-->
<span slot="logContent" slot-scope="text, record">
<j-ellipsis :value="text" :length="40"/>
</span>
</a-table> </a-table>
<!-- table区域-end --> <!-- table区域-end -->
</a-card> </a-card>
...@@ -67,10 +75,14 @@ ...@@ -67,10 +75,14 @@
<script> <script>
import { filterObj } from '@/utils/util'; import { filterObj } from '@/utils/util';
import { JeecgListMixin } from '@/mixins/JeecgListMixin' import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import JEllipsis from '@/components/jeecg/JEllipsis'
export default { export default {
name: "LogList", name: "LogList",
mixins:[JeecgListMixin], mixins:[JeecgListMixin],
components: {
JEllipsis
},
data () { data () {
return { return {
description: '这是日志管理页面', description: '这是日志管理页面',
...@@ -81,6 +93,7 @@ ...@@ -81,6 +93,7 @@
logType:'1', logType:'1',
keyWord:'', keyWord:'',
}, },
tabKey: "1",
// 表头 // 表头
columns: [ columns: [
{ {
...@@ -96,6 +109,7 @@ ...@@ -96,6 +109,7 @@
title: '日志内容', title: '日志内容',
align:"left", align:"left",
dataIndex: 'logContent', dataIndex: 'logContent',
scopedSlots: { customRender: 'logContent' },
sorter: true sorter: true
}, },
{ {
...@@ -143,6 +157,12 @@ ...@@ -143,6 +157,12 @@
sorter: true sorter: true
} }
], ],
operateColumn:
{
title: '操作类型',
dataIndex: 'operateType_dictText',
align:"center",
},
labelCol: { labelCol: {
xs: { span: 1 }, xs: { span: 1 },
sm: { span: 2 }, sm: { span: 2 },
...@@ -177,6 +197,17 @@ ...@@ -177,6 +197,17 @@
}, },
// 日志类型 // 日志类型
callback(key){ callback(key){
// 动态添加操作类型列
if (key == 2) {
this.tabKey = '2';
this.columns.splice(7, 0, this.operateColumn);
}else if(this.columns.length == 9)
{
this.tabKey = '1';
this.columns.splice(7,1);
}
let that=this; let that=this;
that.queryParam.logType=key; that.queryParam.logType=key;
that.loadData(); that.loadData();
......
...@@ -98,7 +98,7 @@ ...@@ -98,7 +98,7 @@
} else if (text == 1) { } else if (text == 1) {
return '菜单' return '菜单'
} else if (text == 2) { } else if (text == 2) {
return '按钮' return '按钮/权限'
} else { } else {
return text return text
} }
......
...@@ -68,13 +68,13 @@ ...@@ -68,13 +68,13 @@
@change="handleTableChange"> @change="handleTableChange">
<span slot="action" slot-scope="text, record"> <span slot="action" slot-scope="text, record">
<a @click="handleEdit(record)">编辑</a> <a v-if="record.sendStatus == 0" @click="handleEdit(record)">编辑</a>
<a-divider type="vertical"/> <a-divider type="vertical" v-if="record.sendStatus == 0"/>
<a-dropdown> <a-dropdown>
<a class="ant-dropdown-link">更多 <a-icon type="down"/></a> <a class="ant-dropdown-link">更多 <a-icon type="down"/></a>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item> <a-menu-item v-if="record.sendStatus != 1">
<a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)"> <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
<a>删除</a> <a>删除</a>
</a-popconfirm> </a-popconfirm>
......
...@@ -125,9 +125,9 @@ ...@@ -125,9 +125,9 @@
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' },
}], }],
url: { url: {
list: "/system/sysAnnouncementSend/getMyAnnouncementSend", list: "/sys/sysAnnouncementSend/getMyAnnouncementSend",
editCementSend:"system/sysAnnouncementSend/editByAnntIdAndUserId", editCementSend:"sys/sysAnnouncementSend/editByAnntIdAndUserId",
readAllMsg:"system/sysAnnouncementSend/readAll", readAllMsg:"sys/sysAnnouncementSend/readAll",
}, },
loading:false, loading:false,
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<!-- 查询区域 --> <!-- 查询区域 -->
<div class="table-page-search-wrapper"> <div class="table-page-search-wrapper">
<a-form layout="inline"> <a-form layout="inline" @submit.prevent="searchQuery">
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :md="6" :sm="12"> <a-col :md="6" :sm="12">
...@@ -117,8 +117,9 @@ ...@@ -117,8 +117,9 @@
</template> </template>
<span slot="action" slot-scope="text, record"> <span slot="action" slot-scope="text, record">
<a @click="handleEdit(record)">编辑</a> <a @click="handleEdit(record)" v-has="'user:edit'">编辑</a>
<a-divider type="vertical"/>
<a-divider type="vertical" v-has="'user:edit'"/>
<a-dropdown> <a-dropdown>
<a class="ant-dropdown-link"> <a class="ant-dropdown-link">
...@@ -140,13 +141,13 @@ ...@@ -140,13 +141,13 @@
</a-menu-item> </a-menu-item>
<a-menu-item v-if="record.status==1"> <a-menu-item v-if="record.status==1">
<a-popconfirm title="确定冻结吗?" @confirm="() => handleFrozen(record.id,2)"> <a-popconfirm title="确定冻结吗?" @confirm="() => handleFrozen(record.id,2,record.username)">
<a>冻结</a> <a>冻结</a>
</a-popconfirm> </a-popconfirm>
</a-menu-item> </a-menu-item>
<a-menu-item v-if="record.status==2"> <a-menu-item v-if="record.status==2">
<a-popconfirm title="确定解冻吗?" @confirm="() => handleFrozen(record.id,1)"> <a-popconfirm title="确定解冻吗?" @confirm="() => handleFrozen(record.id,1,record.username)">
<a>解冻</a> <a>解冻</a>
</a-popconfirm> </a-popconfirm>
</a-menu-item> </a-menu-item>
...@@ -297,6 +298,16 @@ ...@@ -297,6 +298,16 @@
} else { } else {
let ids = ""; let ids = "";
let that = this; let that = this;
let isAdmin = false;
that.selectionRows.forEach(function (row) {
if (row.username == 'admin') {
isAdmin = true;
}
});
if (isAdmin) {
that.$message.warning('管理员账号不允许此操作,请重新选择!');
return;
}
that.selectedRowKeys.forEach(function (val) { that.selectedRowKeys.forEach(function (val) {
ids += val + ","; ids += val + ",";
}); });
...@@ -326,8 +337,13 @@ ...@@ -326,8 +337,13 @@
this.batchFrozen(1); this.batchFrozen(1);
} }
}, },
handleFrozen: function (id, status) { handleFrozen: function (id, status, username) {
let that = this; let that = this;
//TODO 后台校验管理员角色
if ('admin' == username) {
that.$message.warning('管理员账号不允许此操作!');
return;
}
frozenBatch({ids: id, status: status}).then((res) => { frozenBatch({ids: id, status: status}).then((res) => {
if (res.success) { if (res.success) {
that.$message.success(res.message); that.$message.success(res.message);
......
...@@ -105,7 +105,7 @@ ...@@ -105,7 +105,7 @@
mobile:{rules: [{validator:this.validateMobile}]} mobile:{rules: [{validator:this.validateMobile}]}
}, },
url: { url: {
add: "/sysdepart/sysDepart/add", add: "/sys/sysDepart/add",
}, },
} }
}, },
......
...@@ -213,7 +213,7 @@ ...@@ -213,7 +213,7 @@
component:{rules: [{ required: this.show, message: '请输入前端组件!' }]}, component:{rules: [{ required: this.show, message: '请输入前端组件!' }]},
url:{rules: [{ required: this.show, message: '请输入菜单路径!' }]}, url:{rules: [{ required: this.show, message: '请输入菜单路径!' }]},
permsType:{rules: [{ required: true, message: '请输入授权策略!' }]}, permsType:{rules: [{ required: true, message: '请输入授权策略!' }]},
sortNo:{rules: [{initialValue:1.0,validator: this.validateNumber}]}, sortNo:{initialValue:1.0,rules: [{validator: this.validateNumber}]},
} }
} }
}, },
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
@cancel="handleCancel" @cancel="handleCancel"
okText="保存并安排任务" okText="保存并安排任务"
cancelText="关闭"> cancelText="关闭">
<a-spin :spinning="confirmLoading"> <a-spin :spinning="confirmLoading">
<a-form :form="form"> <a-form :form="form">
...@@ -23,11 +23,12 @@ ...@@ -23,11 +23,12 @@
:labelCol="labelCol" :labelCol="labelCol"
:wrapperCol="wrapperCol" :wrapperCol="wrapperCol"
label="cron表达式"> label="cron表达式">
<a-input placeholder="请输入cron表达式" v-decorator="['cronExpression', {'initialValue':'0/1 * * * * ?',rules: [{ required: true, message: '请输入任务类名!' }]}]" /> <!-- <a-input placeholder="请输入cron表达式" v-decorator="['cronExpression', {'initialValue':'0/1 * * * * ?',rules: [{ required: true, message: '请输入任务类名!' }]}]" />-->
<a target="_blank" href="http://cron.qqe2.com/"> <!-- <a target="_blank" href="http://cron.qqe2.com/">-->
<a-icon type="share-alt" /> <!-- <a-icon type="share-alt" />-->
在线cron表达式生成 <!-- 在线cron表达式生成-->
</a> <!-- </a>-->
<j-cron ref="innerVueCron" v-decorator="['cronExpression', {'initialValue':'0/1 * * * * ?',rules: [{ required: true, message: '请输入cron表达式!' }]}]" @change="setCorn"></j-cron>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
:labelCol="labelCol" :labelCol="labelCol"
...@@ -52,7 +53,7 @@ ...@@ -52,7 +53,7 @@
<a-radio-button :value="-1">停止</a-radio-button> <a-radio-button :value="-1">停止</a-radio-button>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-spin> </a-spin>
</a-modal> </a-modal>
...@@ -60,11 +61,15 @@ ...@@ -60,11 +61,15 @@
<script> <script>
import { httpAction } from '@/api/manage' import { httpAction } from '@/api/manage'
import JCron from "@/components/jeecg/JCron.vue";
import pick from 'lodash.pick' import pick from 'lodash.pick'
import moment from "moment" import moment from "moment"
export default { export default {
name: "QuartzJobModal", name: "QuartzJobModal",
components: {
JCron
},
data () { data () {
return { return {
title:"操作", title:"操作",
...@@ -78,10 +83,18 @@ ...@@ -78,10 +83,18 @@
xs: { span: 24 }, xs: { span: 24 },
sm: { span: 16 }, sm: { span: 16 },
}, },
cron: {
label: '',
value: ''
},
confirmLoading: false, confirmLoading: false,
form: this.$form.createForm(this), form: this.$form.createForm(this),
validatorRules:{ validatorRules: {
cron: {
rules: [{
required: true, message: '请输入cron表达式!'
}]
}
}, },
url: { url: {
add: "/sys/quartzJob/add", add: "/sys/quartzJob/add",
...@@ -96,8 +109,7 @@ ...@@ -96,8 +109,7 @@
this.edit({}); this.edit({});
}, },
edit (record) { edit (record) {
this.form.resetFields(); this.model = Object.assign({},record);
this.model = Object.assign({}, record);
console.log(this.model) console.log(this.model)
this.visible = true; this.visible = true;
this.$nextTick(() => { this.$nextTick(() => {
...@@ -113,7 +125,13 @@ ...@@ -113,7 +125,13 @@
const that = this; const that = this;
// 触发表单验证 // 触发表单验证
this.form.validateFields((err, values) => { this.form.validateFields((err, values) => {
console.log('values',values)
if (!err) { if (!err) {
// if (typeof values.cronExpression == "undefined" || Object.keys(values.cronExpression).length==0 ) {
// this.$message.warning('请输入cron表达式!');
// return false;
// }
that.confirmLoading = true; that.confirmLoading = true;
let httpurl = ''; let httpurl = '';
let method = ''; let method = '';
...@@ -122,12 +140,12 @@ ...@@ -122,12 +140,12 @@
method = 'post'; method = 'post';
}else{ }else{
httpurl+=this.url.edit; httpurl+=this.url.edit;
method = 'put'; method = 'put';
} }
let formData = Object.assign(this.model, values); let formData = Object.assign(this.model, values);
//时间格式化 //时间格式化
console.log(formData) console.log('提交参数',formData)
httpAction(httpurl,formData,method).then((res)=>{ httpAction(httpurl,formData,method).then((res)=>{
if(res.success){ if(res.success){
that.$message.success(res.message); that.$message.success(res.message);
...@@ -146,7 +164,24 @@ ...@@ -146,7 +164,24 @@
handleCancel () { handleCancel () {
this.close() this.close()
}, },
setCorn(data){
console.log('data)',data);
this.$nextTick(() => {
this.model.cronExpression = data;
})
// console.log(Object.keys(data).length==0);
// if (Object.keys(data).length==0) {
// this.$message.warning('请输入cron表达式!');
// }
},
validateCron(rule, value, callback){
if(!value){
callback()
}else if (Object.keys(value).length==0) {
callback("请输入cron表达式!");
}
},
} }
} }
......
...@@ -196,8 +196,12 @@ ...@@ -196,8 +196,12 @@
getQueryParams(){ getQueryParams(){
let param = Object.assign({}, this.queryParam,this.isorter); let param = Object.assign({}, this.queryParam,this.isorter);
param.field = this.getQueryField(); param.field = this.getQueryField();
param.current = this.ipagination.current; //--update-begin----author:scott---date:20190818------for:新建公告时指定特定用户翻页错误SelectUserListModal #379----
// param.current = this.ipagination.current;
// param.pageSize = this.ipagination.pageSize;
param.pageNo = this.ipagination.current;
param.pageSize = this.ipagination.pageSize; param.pageSize = this.ipagination.pageSize;
//--update-end----author:scott---date:20190818------for:新建公告时指定特定用户翻页错误SelectUserListModal #379---
return filterObj(param); return filterObj(param);
}, },
getQueryField(){ getQueryField(){
......
...@@ -99,9 +99,9 @@ ...@@ -99,9 +99,9 @@
endTime:{rules: [{ required: true, message: '请输入代理结束时间!' }]}, endTime:{rules: [{ required: true, message: '请输入代理结束时间!' }]},
}, },
url: { url: {
add: "/system/sysUserAgent/add", add: "/sys/sysUserAgent/add",
edit: "/system/sysUserAgent/edit", edit: "/sys/sysUserAgent/edit",
queryByUserName:"/system/sysUserAgent/queryByUserName", queryByUserName:"/sys/sysUserAgent/queryByUserName",
}, },
} }
}, },
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
mode="multiple" mode="multiple"
style="width: 100%" style="width: 100%"
placeholder="请选择用户角色" placeholder="请选择用户角色"
optionFilterProp = "children"
v-model="selectedRole"> v-model="selectedRole">
<a-select-option v-for="(role,roleindex) in roleList" :key="roleindex.toString()" :value="role.id"> <a-select-option v-for="(role,roleindex) in roleList" :key="roleindex.toString()" :value="role.id">
{{ role.roleName }} {{ role.roleName }}
...@@ -411,7 +412,7 @@ ...@@ -411,7 +412,7 @@
if(!value){ if(!value){
callback() callback()
}else{ }else{
if(new RegExp(/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/).test(value)){ if(new RegExp(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/).test(value)){
var params = { var params = {
tableName: 'sys_user', tableName: 'sys_user',
fieldName: 'email', fieldName: 'email',
......
<template> <template>
<a-card :bordered="false"> <a-card :bordered="false" style="width: 160%;text-align: center;margin-left:-25%">
<a-steps class="steps" :current="currentTab"> <a-steps class="steps" :current="currentTab">
<a-step title="账户信息" /> <a-step title="账户信息" />
<a-step title="身份验证" /> <a-step title="身份验证" />
......
...@@ -85,12 +85,12 @@ ...@@ -85,12 +85,12 @@
<a-form-item> <a-form-item>
<a-checkbox v-model="formLogin.rememberMe">自动登陆</a-checkbox> <a-checkbox v-model="formLogin.rememberMe">自动登陆</a-checkbox>
<router-link :to="{ name: 'alteration'}" class="forge-password" style="float: right;"> <!-- <router-link :to="{ name: 'alteration'}" class="forge-password" style="float: right;">
忘记密码 忘记密码
</router-link> </router-link>
<router-link :to="{ name: 'register'}" class="forge-password" style="float: right;margin-right: 10px" > <router-link :to="{ name: 'register'}" class="forge-password" style="float: right;margin-right: 10px" >
注册账户 注册账户
</router-link> </router-link>-->
</a-form-item> </a-form-item>
<a-form-item style="margin-top:24px"> <a-form-item style="margin-top:24px">
...@@ -176,6 +176,8 @@ ...@@ -176,6 +176,8 @@
import { putAction } from '@/api/manage' import { putAction } from '@/api/manage'
import { postAction } from '@/api/manage' import { postAction } from '@/api/manage'
import { encryption , getEncryptedString } from '@/utils/encryption/aesEncrypt' import { encryption , getEncryptedString } from '@/utils/encryption/aesEncrypt'
import store from '@/store/'
import { USER_INFO } from "@/store/mutation-types"
export default { export default {
components: { components: {
...@@ -342,7 +344,9 @@ ...@@ -342,7 +344,9 @@
}) })
}, },
loginSuccess () { loginSuccess () {
this.loginBtn = false // update-begin- author:sunjianlei --- date:20190812 --- for: 登录成功后不解除禁用按钮,防止多次点击
// this.loginBtn = false
// update-end- author:sunjianlei --- date:20190812 --- for: 登录成功后不解除禁用按钮,防止多次点击
this.$router.push({ name: "dashboard" }) this.$router.push({ name: "dashboard" })
this.$notification.success({ this.$notification.success({
message: '欢迎', message: '欢迎',
...@@ -425,6 +429,10 @@ ...@@ -425,6 +429,10 @@
} }
putAction("/sys/selectDepart",obj).then(res=>{ putAction("/sys/selectDepart",obj).then(res=>{
if(res.success){ if(res.success){
const userInfo = res.result.userInfo;
Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000);
store.commit('SET_INFO', userInfo);
//console.log("---切换组织机构---userInfo-------",store.getters.userInfo.orgCode);
this.departClear() this.departClear()
this.loginSuccess() this.loginSuccess()
}else{ }else{
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
<script> <script>
import JGraphicCode from '@/components/jeecg/JGraphicCode' import JGraphicCode from '@/components/jeecg/JGraphicCode'
import { getAction } from '@/api/manage' import { getAction } from '@/api/manage'
import {duplicateCheck } from '@/api/api' import {checkOnlyUser } from '@/api/api'
export default { export default {
name: "Step1", name: "Step1",
components: { components: {
...@@ -116,14 +116,12 @@ ...@@ -116,14 +116,12 @@
callback("请输入用户名和手机号!"); callback("请输入用户名和手机号!");
} }
//判断用户输入账号还是手机号码
if(reg.test(value)){ if(reg.test(value)){
var params = { var params = {
tableName: 'sys_user', phone : value,
fieldName: 'phone',
fieldVal: value,
dataId: null
}; };
duplicateCheck(params).then((res) => { checkOnlyUser(params).then((res) => {
if (res.success) { if (res.success) {
callback("用户名不存在!") callback("用户名不存在!")
} else { } else {
...@@ -132,18 +130,15 @@ ...@@ -132,18 +130,15 @@
}) })
}else{ }else{
var params = { var params = {
tableName: 'sys_user', username: value,
fieldName: 'username',
fieldVal: value,
dataId: null
}; };
duplicateCheck(params).then((res) => { checkOnlyUser(params).then((res) => {
if (res.success) { if (res.success) {
callback("用户名不存在!") callback("用户名不存在!")
} else { } else {
callback() callback()
} }
}) })
} }
}, },
......
...@@ -30,22 +30,22 @@ ...@@ -30,22 +30,22 @@
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
label="验证码"
:labelCol="{span: 5}" :labelCol="{span: 5}"
:wrapperCol="{span: 19}" :wrapperCol="{span: 19}"
style="margin-left:106px"
v-if="show"> v-if="show">
<a-row :gutter="16" > <a-row :gutter="16" style="margin-left: 35px">
<a-col class="gutter-row" :span="16"> <a-col class="gutter-row" :span="10">
<a-input <a-input
v-decorator="['captcha',validatorRules.captcha]" v-decorator="['captcha',validatorRules.captcha]"
type="text" type="text"
placeholder="请输入验证码" > placeholder="手机短信验证码" >
</a-input> </a-input>
</a-col> </a-col>
<a-col class="gutter-row" :span="8" > <a-col class="gutter-row" :span="8" >
<a-button <a-button
class="getCaptcha"
tabindex="-1" tabindex="-1"
size="default"
:disabled="state.smsSendBtn" :disabled="state.smsSendBtn"
@click.stop.prevent="getCaptcha" @click.stop.prevent="getCaptcha"
v-text="!state.smsSendBtn && '获取验证码' || (state.time+' s')"></a-button> v-text="!state.smsSendBtn && '获取验证码' || (state.time+' s')"></a-button>
......
...@@ -21,8 +21,12 @@ module.exports = { ...@@ -21,8 +21,12 @@ module.exports = {
} }
}, },
*/ */
configureWebpack: {}, configureWebpack: config => {
//生产环境取消 console.log
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
}
},
chainWebpack: (config) => { chainWebpack: (config) => {
config.resolve.alias config.resolve.alias
.set('@$', resolve('src')) .set('@$', resolve('src'))
......
...@@ -841,6 +841,43 @@ ...@@ -841,6 +841,43 @@
cssnano-preset-default "^4.0.0" cssnano-preset-default "^4.0.0"
postcss "^7.0.0" postcss "^7.0.0"
"@jeecg/antd-onine@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@jeecg/antd-onine/-/antd-onine-1.0.1.tgz#bc9e54e75e8e0eff3ee361a1c022cb8672357a8d"
integrity sha512-l9HuoxX8sQ/XQZQfpTDnWuuqqfd2RY4EwOvf0SG45eOk6mDYzaR4P0BRT4QEC59lT3cRu5w5rKH2dn3RHEFzUw==
dependencies:
"@antv/data-set" "^0.10.2"
"@tinymce/tinymce-vue" "^2.0.0"
ant-design-vue "^1.3.9"
apexcharts "^3.6.5"
axios "^0.18.0"
clipboard "^2.0.4"
codemirror "^5.46.0"
dayjs "^1.8.0"
enquire.js "^2.1.6"
js-cookie "^2.2.0"
lodash.get "^4.4.2"
lodash.pick "^4.4.0"
md5 "^2.2.1"
nprogress "^0.2.0"
tinymce "^5.0.2"
viser-vue "^2.4.4"
vue "^2.6.10"
vue-apexcharts "^1.3.2"
vue-class-component "^6.0.0"
vue-cropper "^0.4.8"
vue-i18n "^8.7.0"
vue-loader "^15.7.0"
vue-ls "^3.2.0"
vue-photo-preview "^1.1.3"
vue-print-nb-jeecg "^1.0.8"
vue-property-decorator "^7.3.0"
vue-router "^3.0.1"
vue-splitpane "^1.0.4"
vuedraggable "^2.20.0"
vuex "^3.0.1"
vuex-class "^0.3.1"
"@mrmlnc/readdir-enhanced@^2.2.1": "@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1" version "2.2.1"
resolved "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" resolved "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
......
Jeecg-Boot 快速开发平台 Jeecg-Boot 快速开发平台
=============== ===============
当前最新版本: 2.0.2(发布日期:20190708 当前最新版本: 2.1.0(发布日期:20190826
## 后端技术架构 ## 后端技术架构
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
-- 创建mysql库
create database `jeecg-boot-os` default character set utf8mb4 collate utf8mb4_general_ci;
\ No newline at end of file
-- author:scott -- date:20190522-- for: 菜单根请求URL配置错误 ---------
-- author:scott -- date:20190522-- for: 菜单根请求URL配置错误 ---------
UPDATE `sys_permission` SET `id`='190c2b43bec6a5f7a4194a85db67d96a', `parent_id`='d7d6e2e4e2934f2c9385a623fd98c6f3', `name`='角色维护', `url`='/isystem/roleUserList', `component`='system/RoleUserList', `component_name`=NULL, `redirect`=NULL, `menu_type`='1', `perms`=NULL, `perms_type`=NULL, `sort_no`='1', `always_show`='0', `icon`=NULL, `is_route`='1', `is_leaf`='1', `hidden`='0', `description`=NULL, `create_by`='admin', `create_time`='2019-04-17 15:13:56', `update_by`=NULL, `update_time`=NULL, `del_flag`='0', `rule_flag`='0', `status`=NULL WHERE (`id`='190c2b43bec6a5f7a4194a85db67d96a');
UPDATE `sys_permission` SET `id`='5c2f42277948043026b7a14692456828', `parent_id`='d7d6e2e4e2934f2c9385a623fd98c6f3', `name`='我的部门', `url`='/isystem/departUserList', `component`='system/DepartUserList', `component_name`=NULL, `redirect`=NULL, `menu_type`='1', `perms`=NULL, `perms_type`=NULL, `sort_no`='1', `always_show`='0', `icon`=NULL, `is_route`='1', `is_leaf`='1', `hidden`='0', `description`=NULL, `create_by`='admin', `create_time`='2019-04-17 15:12:24', `update_by`=NULL, `update_time`=NULL, `del_flag`='0', `rule_flag`='0', `status`=NULL WHERE (`id`='5c2f42277948043026b7a14692456828');
-- author:scott -- date:20190522-- for: 菜单根请求URL配置错误 ----------
-- author:scott -- date:20190524-- for: 字段长度不够 ----------
ALTER TABLE `sys_log`
MODIFY COLUMN `request_param` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求参数' AFTER `request_url`;
-- author:scott -- date:20190524-- for: 字段长度不够 ----------
-- author:taoyan -- date:20190529-- for: 分类字典
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_route`, `is_leaf`, `hidden`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`) VALUES ('ebb9d82ea16ad864071158e0c449d186', 'd7d6e2e4e2934f2c9385a623fd98c6f3', '分类字典', '/isys/category', 'system/SysCategoryList', NULL, NULL, '1', NULL, '1', '5', '0', NULL, '1', '1', '0', NULL, 'admin', '2019-05-29 18:48:07', 'admin', '2019-05-29 18:48:27', '0', '0', '1');
CREATE TABLE `sys_category` (
`id` varchar(36) NOT NULL,
`pid` varchar(36) DEFAULT NULL COMMENT '父级节点',
`name` varchar(100) DEFAULT NULL COMMENT '类型名称',
`code` varchar(100) DEFAULT NULL COMMENT '类型编码',
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新日期',
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '所属部门',
`has_child` varchar(3) DEFAULT NULL COMMENT '是否有子节点',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- author:taoyan -- date:20190529-- for: 分类字典
-- author:scott---date:20190531 -----for:字典表del_flag代码不规范问题
update sys_dict set del_flag = 0 where del_flag=1;
update sys_dict set del_flag = 1 where del_flag=2;
delete from sys_dict_item where dict_id not in (select id from sys_dict);
-- author:scott---date:20190531 -----for:字典表del_flag代码不规范问题
-- author:taoyan -- date:20190611 -- for: 树形列表菜单新增字典
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `type`) VALUES ('a7adbcd86c37f7dbc9b66945c82ef9e6', '1是0否', 'yn', '', '1', 'admin', '2019-05-22 19:29:29', NULL, NULL, '0');
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('51222413e5906cdaf160bb5c86fb827c', 'a7adbcd86c37f7dbc9b66945c82ef9e6', '是', '1', '', '1.00', '1', 'admin', '2019-05-22 19:29:45', NULL, NULL);
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('c5700a71ad08994d18ad1dacc37a71a9', 'a7adbcd86c37f7dbc9b66945c82ef9e6', '否', '0', '', '1.00', '1', 'admin', '2019-05-22 19:29:55', NULL, NULL);
-- author:taoyan -- date:20190611 -- for: 树形列表菜单新增字典
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jeecg-boot-base-common</artifactId> <artifactId>jeecg-boot-base-common</artifactId>
<version>2.0.2</version> <version>2.1.0</version>
<parent> <parent>
<groupId>org.jeecgframework.boot</groupId> <groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-parent</artifactId> <artifactId>jeecg-boot-parent</artifactId>
<version>2.0.2</version> <version>2.1.0</version>
</parent> </parent>
<repositories> <repositories>
......
...@@ -33,4 +33,11 @@ public @interface AutoLog { ...@@ -33,4 +33,11 @@ public @interface AutoLog {
* @return 0:操作日志;1:登录日志;2:定时任务; * @return 0:操作日志;1:登录日志;2:定时任务;
*/ */
int logType() default CommonConstant.LOG_TYPE_2; int logType() default CommonConstant.LOG_TYPE_2;
/**
* 操作日志类型
*
* @return (1查询,2添加,3修改,4删除)
*/
int operateType() default 0;
} }
...@@ -22,5 +22,15 @@ public interface CacheConstant { ...@@ -22,5 +22,15 @@ public interface CacheConstant {
*/ */
public static final String LOGIN_USER_RULES_CACHE = "loginUser_cacheRules"; public static final String LOGIN_USER_RULES_CACHE = "loginUser_cacheRules";
/**
* 部门信息缓存
*/
public static final String DEPART_INFO_CACHE = "departCache_info";
/**
* 部门id信息缓存
*/
public static final String DEPART_IDMODEL_CACHE = "departCache_idmodel";
} }
...@@ -26,11 +26,41 @@ public interface CommonConstant { ...@@ -26,11 +26,41 @@ public interface CommonConstant {
* 系统日志类型: 登录 * 系统日志类型: 登录
*/ */
public static final int LOG_TYPE_1 = 1; public static final int LOG_TYPE_1 = 1;
/** /**
* 系统日志类型: 操作 * 系统日志类型: 操作
*/ */
public static final int LOG_TYPE_2 = 2; public static final int LOG_TYPE_2 = 2;
/**
* 操作日志类型: 查询
*/
public static final int OPERATE_TYPE_1 = 1;
/**
* 操作日志类型: 添加
*/
public static final int OPERATE_TYPE_2 = 2;
/**
* 操作日志类型: 更新
*/
public static final int OPERATE_TYPE_3 = 3;
/**
* 操作日志类型: 删除
*/
public static final int OPERATE_TYPE_4 = 4;
/**
* 操作日志类型: 倒入
*/
public static final int OPERATE_TYPE_5 = 5;
/**
* 操作日志类型: 导出
*/
public static final int OPERATE_TYPE_6 = 6;
/** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */ /** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */
......
...@@ -12,12 +12,15 @@ public interface DataBaseConstant { ...@@ -12,12 +12,15 @@ public interface DataBaseConstant {
/** /**
* 数据-所属机构编码 * 数据-所属机构编码
*/ */
public static final String SYS_ORG_CODE_TABLE = "sys_org_code";
/**
* 数据-所属机构编码
*/
public static final String SYS_MULTI_ORG_CODE = "sysMultiOrgCode"; public static final String SYS_MULTI_ORG_CODE = "sysMultiOrgCode";
/** /**
* 数据-所属机构编码 * 数据-所属机构编码
*/ */
public static final String SYS_ORG_CODE_TABLE = "sys_org_code"; public static final String SYS_MULTI_ORG_CODE_TABLE = "sys_multi_org_code";
/** /**
* 数据-系统用户编码(对应登录用户账号) * 数据-系统用户编码(对应登录用户账号)
*/ */
......
...@@ -7,6 +7,7 @@ import org.springframework.dao.DuplicateKeyException; ...@@ -7,6 +7,7 @@ import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.NoHandlerFoundException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
...@@ -64,5 +65,14 @@ public class JeecgBootExceptionHandler { ...@@ -64,5 +65,14 @@ public class JeecgBootExceptionHandler {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
return Result.error("没有权限,请联系管理员授权"); return Result.error("没有权限,请联系管理员授权");
} }
/**
* spring默认上传大小100MB 超出大小捕获异常MaxUploadSizeExceededException
*/
@ExceptionHandler(MaxUploadSizeExceededException.class)
public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
log.error(e.getMessage(), e);
return Result.error("文件大小超出10MB限制, 请压缩或降低文件质量! ");
}
} }
...@@ -55,7 +55,7 @@ public class QueryGenerator { ...@@ -55,7 +55,7 @@ public class QueryGenerator {
private static SimpleDateFormat getTime(){ private static SimpleDateFormat getTime(){
SimpleDateFormat time = local.get(); SimpleDateFormat time = local.get();
if(time == null){ if(time == null){
time = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
local.set(time); local.set(time);
} }
return time; return time;
......
...@@ -155,7 +155,7 @@ public class JwtUtil { ...@@ -155,7 +155,7 @@ public class JwtUtil {
} }
} }
//替换为系统登录用户真实名字 //替换为系统登录用户真实名字
if (key.equals(DataBaseConstant.SYS_USER_NAME)|| key.equals(DataBaseConstant.SYS_USER_NAME_TABLE)) { else if (key.equals(DataBaseConstant.SYS_USER_NAME)|| key.equals(DataBaseConstant.SYS_USER_NAME_TABLE)) {
if(user==null) { if(user==null) {
returnValue = sysUser.getRealname(); returnValue = sysUser.getRealname();
}else { }else {
...@@ -164,7 +164,7 @@ public class JwtUtil { ...@@ -164,7 +164,7 @@ public class JwtUtil {
} }
//替换为系统用户登录所使用的机构编码 //替换为系统用户登录所使用的机构编码
if (key.equals(DataBaseConstant.SYS_ORG_CODE)|| key.equals(DataBaseConstant.SYS_ORG_CODE_TABLE)) { else if (key.equals(DataBaseConstant.SYS_ORG_CODE)|| key.equals(DataBaseConstant.SYS_ORG_CODE_TABLE)) {
if(user==null) { if(user==null) {
returnValue = sysUser.getOrgCode(); returnValue = sysUser.getOrgCode();
}else { }else {
...@@ -172,7 +172,7 @@ public class JwtUtil { ...@@ -172,7 +172,7 @@ public class JwtUtil {
} }
} }
//替换为系统用户所拥有的所有机构编码 //替换为系统用户所拥有的所有机构编码
if (key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE)|| key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE)) { else if (key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE)|| key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE_TABLE)) {
if(user.isOneDepart()) { if(user.isOneDepart()) {
returnValue = user.getSysMultiOrgCode().get(0); returnValue = user.getSysMultiOrgCode().get(0);
}else { }else {
...@@ -180,18 +180,23 @@ public class JwtUtil { ...@@ -180,18 +180,23 @@ public class JwtUtil {
} }
} }
//替换为当前系统时间(年月日) //替换为当前系统时间(年月日)
if (key.equals(DataBaseConstant.SYS_DATE)|| key.equals(DataBaseConstant.SYS_DATE_TABLE)) { else if (key.equals(DataBaseConstant.SYS_DATE)|| key.equals(DataBaseConstant.SYS_DATE_TABLE)) {
returnValue = user.getSysDate(); returnValue = user.getSysDate();
} }
//替换为当前系统时间(年月日时分秒) //替换为当前系统时间(年月日时分秒)
if (key.equals(DataBaseConstant.SYS_TIME)|| key.equals(DataBaseConstant.SYS_TIME_TABLE)) { else if (key.equals(DataBaseConstant.SYS_TIME)|| key.equals(DataBaseConstant.SYS_TIME_TABLE)) {
returnValue = user.getSysTime(); returnValue = user.getSysTime();
} }
//流程状态默认值(默认未发起) //流程状态默认值(默认未发起)
if (key.equals(DataBaseConstant.BPM_STATUS_TABLE)|| key.equals(DataBaseConstant.BPM_STATUS_TABLE)) { else if (key.equals(DataBaseConstant.BPM_STATUS)|| key.equals(DataBaseConstant.BPM_STATUS_TABLE)) {
returnValue = "1"; returnValue = "1";
} }
if(returnValue!=null){returnValue = returnValue + moshi;} if(returnValue!=null){returnValue = returnValue + moshi;}
return returnValue; return returnValue;
} }
public static void main(String[] args) {
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjUzMzY1MTMsInVzZXJuYW1lIjoiYWRtaW4ifQ.xjhud_tWCNYBOg_aRlMgOdlZoWFFKB_givNElHNw3X0";
System.out.println(JwtUtil.getUsername(token));
}
} }
...@@ -2,6 +2,8 @@ package org.jeecg.common.system.vo; ...@@ -2,6 +2,8 @@ package org.jeecg.common.system.vo;
import java.io.Serializable; import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
...@@ -9,6 +11,7 @@ import lombok.experimental.Accessors; ...@@ -9,6 +11,7 @@ import lombok.experimental.Accessors;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Accessors(chain = true) @Accessors(chain = true)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DictModel implements Serializable{ public class DictModel implements Serializable{
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
......
...@@ -38,6 +38,11 @@ public class LoginUser { ...@@ -38,6 +38,11 @@ public class LoginUser {
*/ */
private String realname; private String realname;
/**
* 登录人密码
*/
private String password;
/** /**
* 当前登录部门code * 当前登录部门code
*/ */
......
package org.jeecg.common.util;
import org.apache.commons.lang.StringUtils;
public enum DySmsEnum {
LOGIN_TEMPLATE_CODE("SMS_167040816","JEECG","code"),
FORGET_PASSWORD_TEMPLATE_CODE("SMS_167040816","JEECG","code"),
REGISTER_TEMPLATE_CODE("SMS_144146309","JEECG","code");
/**
* 短信模板编码
*/
private String templateCode;
/**
* 签名
*/
private String signName;
/**
* 短信模板必需的数据名称,多个key以逗号分隔,此处配置作为校验
*/
private String keys;
private DySmsEnum(String templateCode,String signName,String keys) {
this.templateCode = templateCode;
this.signName = signName;
this.keys = keys;
}
public String getTemplateCode() {
return templateCode;
}
public void setTemplateCode(String templateCode) {
this.templateCode = templateCode;
}
public String getSignName() {
return signName;
}
public void setSignName(String signName) {
this.signName = signName;
}
public String getKeys() {
return keys;
}
public void setKeys(String keys) {
this.keys = keys;
}
public static DySmsEnum toEnum(String templateCode) {
if(StringUtils.isEmpty(templateCode)){
return null;
}
for(DySmsEnum item : DySmsEnum.values()) {
if(item.getTemplateCode().equals(templateCode)) {
return item;
}
}
return null;
}
}
...@@ -2,6 +2,8 @@ package org.jeecg.common.util; ...@@ -2,6 +2,8 @@ package org.jeecg.common.util;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient; import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
...@@ -31,32 +33,27 @@ public class DySmsHelper { ...@@ -31,32 +33,27 @@ public class DySmsHelper {
static final String domain = "dysmsapi.aliyuncs.com"; static final String domain = "dysmsapi.aliyuncs.com";
// TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找) // TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)
static final String accessKeyId = "?"; static String accessKeyId;
static final String accessKeySecret = "?"; static String accessKeySecret;
/** public static void setAccessKeyId(String accessKeyId) {
* 登陆时采用的短信发送模板编码 DySmsHelper.accessKeyId = accessKeyId;
*/ }
public static final String LOGIN_TEMPLATE_CODE="SMS_167040816";
public static void setAccessKeySecret(String accessKeySecret) {
/** DySmsHelper.accessKeySecret = accessKeySecret;
* 忘记密码时采用的短信发送模板编码 }
*/
public static final String FORGET_PASSWORD_TEMPLATE_CODE="SMS_167040816"; public static String getAccessKeyId() {
return accessKeyId;
}
/**
* 注册时采用的短信发送模板编码 public static String getAccessKeySecret() {
*/ return accessKeySecret;
public static final String REGISTER_TEMPLATE_CODE="SMS_144146309"; }
/**
* 必填:短信签名-可在短信控制台中找到
*/
public static final String signName="JEECG";
public static boolean sendSms(String phone,String code,String templateCode) throws ClientException { public static boolean sendSms(String phone,JSONObject templateParamJson,DySmsEnum dySmsEnum) throws ClientException {
//可自助调整超时时间 //可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000");
...@@ -66,16 +63,19 @@ public class DySmsHelper { ...@@ -66,16 +63,19 @@ public class DySmsHelper {
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile); IAcsClient acsClient = new DefaultAcsClient(profile);
//验证json参数
validateParam(templateParamJson,dySmsEnum);
//组装请求对象-具体描述见控制台-文档部分内容 //组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest(); SendSmsRequest request = new SendSmsRequest();
//必填:待发送手机号 //必填:待发送手机号
request.setPhoneNumbers(phone); request.setPhoneNumbers(phone);
//必填:短信签名-可在短信控制台中找到 //必填:短信签名-可在短信控制台中找到
request.setSignName(signName); request.setSignName(dySmsEnum.getSignName());
//必填:短信模板-可在短信控制台中找到 //必填:短信模板-可在短信控制台中找到
request.setTemplateCode("SMS_167040816"); request.setTemplateCode(dySmsEnum.getTemplateCode());
//可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为 //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
request.setTemplateParam("{\"code\":\""+code+"\"}"); request.setTemplateParam(templateParamJson.toJSONString());
//选填-上行短信扩展码(无特殊需求用户请忽略此字段) //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
//request.setSmsUpExtendCode("90997"); //request.setSmsUpExtendCode("90997");
...@@ -96,10 +96,21 @@ public class DySmsHelper { ...@@ -96,10 +96,21 @@ public class DySmsHelper {
} }
private static void validateParam(JSONObject templateParamJson,DySmsEnum dySmsEnum) {
String keys = dySmsEnum.getKeys();
String [] keyArr = keys.split(",");
for(String item :keyArr) {
if(!templateParamJson.containsKey(item)) {
throw new RuntimeException("模板缺少参数:"+item);
}
}
}
public static void main(String[] args) throws ClientException, InterruptedException { public static void main(String[] args) throws ClientException, InterruptedException {
JSONObject obj = new JSONObject();
sendSms("13800138000", "123456", FORGET_PASSWORD_TEMPLATE_CODE); obj.put("code", "1234");
sendSms("13800138000", obj, DySmsEnum.FORGET_PASSWORD_TEMPLATE_CODE);
} }
} }
...@@ -88,8 +88,9 @@ public class PasswordUtil { ...@@ -88,8 +88,9 @@ public class PasswordUtil {
Cipher cipher = Cipher.getInstance(ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
//update-begin-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
encipheredData = cipher.doFinal(plaintext.getBytes()); encipheredData = cipher.doFinal(plaintext.getBytes("utf-8"));
//update-end-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
} catch (Exception e) { } catch (Exception e) {
} }
return bytesToHexString(encipheredData); return bytesToHexString(encipheredData);
......
...@@ -62,7 +62,30 @@ public class SqlInjectionUtil { ...@@ -62,7 +62,30 @@ public class SqlInjectionUtil {
*/ */
@Deprecated @Deprecated
public static void specialFilterContent(String value) { public static void specialFilterContent(String value) {
String specialXssStr = "exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|+|,"; String specialXssStr = "exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|+|";
String[] xssArr = specialXssStr.split("\\|");
if (value == null || "".equals(value)) {
return;
}
value = value.toLowerCase();// 统一转为小写
for (int i = 0; i < xssArr.length; i++) {
if (value.indexOf(xssArr[i]) > -1) {
log.error("请注意,值可能存在SQL注入风险!---> {}", value);
throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
}
}
return;
}
/**
* @特殊方法(不通用) 仅用于Online报表SQL解析,注入过滤
* @param value
* @return
*/
@Deprecated
public static void specialFilterContentForOnlineReport(String value) {
String specialXssStr = "exec |insert |delete |update |drop |chr |mid |master |truncate |char |declare |";
String[] xssArr = specialXssStr.split("\\|"); String[] xssArr = specialXssStr.split("\\|");
if (value == null || "".equals(value)) { if (value == null || "".equals(value)) {
return; return;
......
package org.jeecg.common.util.security;
import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONObject;
import org.jeecg.common.util.security.entity.*;
import javax.crypto.SecretKey;
import java.security.KeyPair;
public class SecurityTools {
public static final String ALGORITHM = "AES/ECB/PKCS5Padding";
public static SecurityResp valid(SecurityReq req) {
SecurityResp resp=new SecurityResp();
String pubKey=req.getPubKey();
String aesKey=req.getAesKey();
String data=req.getData();
String signData=req.getSignData();
RSA rsa=new RSA(null, Base64Decoder.decode(pubKey));
Sign sign= new Sign(SignAlgorithm.SHA1withRSA,null,pubKey);
byte[] decryptAes = rsa.decrypt(aesKey, KeyType.PublicKey);
//log.info("rsa解密后的秘钥"+ Base64Encoder.encode(decryptAes));
AES aes = SecureUtil.aes(decryptAes);
String dencrptValue =aes.decryptStr(data);
//log.info("解密后报文"+dencrptValue);
resp.setData(new JSONObject(dencrptValue));
boolean verify = sign.verify(dencrptValue.getBytes(), Base64Decoder.decode(signData));
resp.setSuccess(verify);
return resp;
}
public static SecuritySignResp sign(SecuritySignReq req) {
SecretKey secretKey = SecureUtil.generateKey(ALGORITHM);
byte[] key= secretKey.getEncoded();
String prikey=req.getPrikey();
String data=req.getData();
AES aes = SecureUtil.aes(key);
aes.getSecretKey().getEncoded();
String encrptData =aes.encryptBase64(data);
RSA rsa=new RSA(prikey,null);
byte[] encryptAesKey = rsa.encrypt(secretKey.getEncoded(), KeyType.PrivateKey);
//log.info(("rsa加密过的秘钥=="+Base64Encoder.encode(encryptAesKey));
Sign sign= new Sign(SignAlgorithm.SHA1withRSA,prikey,null);
byte[] signed = sign.sign(data.getBytes());
//log.info(("签名数据===》》"+Base64Encoder.encode(signed));
SecuritySignResp resp=new SecuritySignResp();
resp.setAesKey(Base64Encoder.encode(encryptAesKey));
resp.setData(encrptData);
resp.setSignData(Base64Encoder.encode(signed));
return resp;
}
public static MyKeyPair generateKeyPair(){
KeyPair keyPair= SecureUtil.generateKeyPair(SignAlgorithm.SHA1withRSA.getValue(),2048);
String priKey= Base64Encoder.encode(keyPair.getPrivate().getEncoded());
String pubkey= Base64Encoder.encode(keyPair.getPublic().getEncoded());
MyKeyPair resp=new MyKeyPair();
resp.setPriKey(priKey);
resp.setPubKey(pubkey);
return resp;
}
}
package org.jeecg.common.util.security.entity;
import lombok.Data;
@Data
public class MyKeyPair {
private String priKey;
private String pubKey;
}
package org.jeecg.common.util.security.entity;
import lombok.Data;
@Data
public class SecurityReq {
private String data;
private String pubKey;
private String signData;
private String aesKey;
}
package org.jeecg.common.util.security.entity;
import cn.hutool.json.JSONObject;
import lombok.Data;
@Data
public class SecurityResp {
private Boolean success;
private JSONObject data;
}
package org.jeecg.common.util.security.entity;
import lombok.Data;
@Data
public class SecuritySignReq {
private String data;
private String prikey;
}
package org.jeecg.common.util.security.entity;
import lombok.Data;
@Data
public class SecuritySignResp {
private String data;
private String signData;
private String aesKey;
}
*.js linguist-language=Java
*.css linguist-language=Java
*.html linguist-language=Java
*.vue linguist-language=Java
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jeecg-boot-module-system</artifactId> <artifactId>jeecg-boot-module-system</artifactId>
<version>2.0.2</version> <version>2.1.0</version>
<parent> <parent>
<groupId>org.jeecgframework.boot</groupId> <groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-parent</artifactId> <artifactId>jeecg-boot-parent</artifactId>
<version>2.0.2</version> <version>2.1.0</version>
</parent> </parent>
<repositories> <repositories>
...@@ -35,6 +35,24 @@ ...@@ -35,6 +35,24 @@
<groupId>org.jeecgframework.boot</groupId> <groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base-common</artifactId> <artifactId>jeecg-boot-base-common</artifactId>
</dependency> </dependency>
<!-- online form-->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>online-form</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- online form -->
</dependencies> </dependencies>
<build> <build>
......
package org.jeecg; package org.jeecg;
import java.net.InetAddress; import lombok.extern.slf4j.Slf4j;
import java.net.UnknownHostException;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import lombok.extern.slf4j.Slf4j;
import springfox.documentation.swagger2.annotations.EnableSwagger2; import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Slf4j @Slf4j
@EnableSwagger2 @EnableSwagger2
@SpringBootApplication @SpringBootApplication
public class JeecgApplication { public class JeecgApplication {
public static void main(String[] args) throws UnknownHostException { public static void main(String[] args) throws UnknownHostException {
//System.setProperty("spring.devtools.restart.enabled", "true"); //System.setProperty("spring.devtools.restart.enabled", "true");
ConfigurableApplicationContext application = SpringApplication.run(JeecgApplication.class, args);
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = env.getProperty("server.servlet.context-path");
log.info("\n----------------------------------------------------------\n\t" +
"Application Jeecg-Boot is running! Access URLs:\n\t" +
"Local: \t\thttp://localhost:" + port + path + "/\n\t" +
"External: \thttp://" + ip + ":" + port + path + "/\n\t" +
"swagger-ui: \thttp://" + ip + ":" + port + path + "/swagger-ui.html\n\t" +
"Doc: \t\thttp://" + ip + ":" + port + path + "/doc.html\n" +
"----------------------------------------------------------");
} ConfigurableApplicationContext application = SpringApplication.run(JeecgApplication.class, args);
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = env.getProperty("server.servlet.context-path");
log.info("\n----------------------------------------------------------\n\t" +
"Application Jeecg-Boot is running! Access URLs:\n\t" +
"Local: \t\thttp://localhost:" + port + path + "/\n\t" +
"External: \thttp://" + ip + ":" + port + path + "/\n\t" +
"swagger-ui: \thttp://" + ip + ":" + port + path + "/swagger-ui.html\n\t" +
"Doc: \t\thttp://" + ip + ":" + port + path + "/doc.html\n" +
"----------------------------------------------------------");
}
} }
\ No newline at end of file
...@@ -17,7 +17,9 @@ import org.springframework.data.redis.cache.RedisCacheManager; ...@@ -17,7 +17,9 @@ import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
...@@ -82,13 +84,18 @@ public class RedisConfig extends CachingConfigurerSupport { ...@@ -82,13 +84,18 @@ public class RedisConfig extends CachingConfigurerSupport {
*/ */
@Bean @Bean
public CacheManager cacheManager(LettuceConnectionFactory factory) { public CacheManager cacheManager(LettuceConnectionFactory factory) {
// 配置序列化
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
// 以锁写入的方式创建RedisCacheWriter对象 // 以锁写入的方式创建RedisCacheWriter对象
RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory); //RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
// 创建默认缓存配置对象 // 创建默认缓存配置对象
/* 默认配置,设置缓存有效期 1小时*/ /* 默认配置,设置缓存有效期 1小时*/
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1)); //RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
/* 配置test的超时时间为120s*/ /* 配置test的超时时间为120s*/
RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter(lettuceConnectionFactory)).cacheDefaults(defaultCacheConfig) RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter(factory)).cacheDefaults(redisCacheConfiguration)
.withInitialCacheConfigurations(singletonMap("test", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(120)).disableCachingNullValues())) .withInitialCacheConfigurations(singletonMap("test", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(120)).disableCachingNullValues()))
.transactionAware().build(); .transactionAware().build();
return cacheManager; return cacheManager;
......
...@@ -42,8 +42,11 @@ public class ShiroConfig { ...@@ -42,8 +42,11 @@ public class ShiroConfig {
shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器 // 拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
//cas验证登录
filterChainDefinitionMap.put("/cas/client/validateLogin", "anon");
// 配置不会被拦截的链接 顺序判断 // 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/sys/login", "anon"); //登录接口排除 filterChainDefinitionMap.put("/sys/login", "anon"); //登录接口排除
filterChainDefinitionMap.put("/sys/logout", "anon"); //登出接口排除
filterChainDefinitionMap.put("/sys/getEncryptedString", "anon"); //获取加密串 filterChainDefinitionMap.put("/sys/getEncryptedString", "anon"); //获取加密串
filterChainDefinitionMap.put("/sys/sms", "anon");//短信验证码 filterChainDefinitionMap.put("/sys/sms", "anon");//短信验证码
filterChainDefinitionMap.put("/sys/phoneLogin", "anon");//手机登录 filterChainDefinitionMap.put("/sys/phoneLogin", "anon");//手机登录
...@@ -63,9 +66,16 @@ public class ShiroConfig { ...@@ -63,9 +66,16 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/**/*.css", "anon"); filterChainDefinitionMap.put("/**/*.css", "anon");
filterChainDefinitionMap.put("/**/*.html", "anon"); filterChainDefinitionMap.put("/**/*.html", "anon");
filterChainDefinitionMap.put("/**/*.svg", "anon"); filterChainDefinitionMap.put("/**/*.svg", "anon");
filterChainDefinitionMap.put("/**/*.pdf", "anon");
filterChainDefinitionMap.put("/**/*.jpg", "anon"); filterChainDefinitionMap.put("/**/*.jpg", "anon");
filterChainDefinitionMap.put("/**/*.png", "anon"); filterChainDefinitionMap.put("/**/*.png", "anon");
filterChainDefinitionMap.put("/**/*.ico", "anon"); filterChainDefinitionMap.put("/**/*.ico", "anon");
// update-begin--Author:sunjianlei Date:20190813 for:排除字体格式的后缀
filterChainDefinitionMap.put("/**/*.ttf", "anon");
filterChainDefinitionMap.put("/**/*.woff", "anon");
// update-begin--Author:sunjianlei Date:20190813 for:排除字体格式的后缀
filterChainDefinitionMap.put("/druid/**", "anon"); filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/swagger-ui.html", "anon"); filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger**/**", "anon"); filterChainDefinitionMap.put("/swagger**/**", "anon");
...@@ -76,7 +86,20 @@ public class ShiroConfig { ...@@ -76,7 +86,20 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/actuator/metrics/**", "anon"); filterChainDefinitionMap.put("/actuator/metrics/**", "anon");
filterChainDefinitionMap.put("/actuator/httptrace/**", "anon"); filterChainDefinitionMap.put("/actuator/httptrace/**", "anon");
filterChainDefinitionMap.put("/actuator/redis/**", "anon"); filterChainDefinitionMap.put("/actuator/redis/**", "anon");
filterChainDefinitionMap.put("/test/jeecgDemo/demo3", "anon"); //模板测试
filterChainDefinitionMap.put("/test/jeecgDemo/redisDemo/**", "anon"); //redis测试
//排除Online请求
filterChainDefinitionMap.put("/auto/cgform/**", "anon");
//websocket排除
filterChainDefinitionMap.put("/websocket/**", "anon");
// 添加自己的过滤器并且取名为jwt // 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1); Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
filterMap.put("jwt", new JwtFilter()); filterMap.put("jwt", new JwtFilter());
......
package org.jeecg.config;
import org.jeecg.common.util.DySmsHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 设置静态参数初始化
*/
@Configuration
public class StaticConfig {
@Value("${jeecg.sms.accessKeyId}")
private String accessKeyId;
@Value("${jeecg.sms.accessKeySecret}")
private String accessKeySecret;
@Bean
public void initStatic() {
DySmsHelper.setAccessKeyId(accessKeyId);
DySmsHelper.setAccessKeySecret(accessKeySecret);
}
}
package org.jeecg.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/**
* 注入ServerEndpointExporter,
* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
...@@ -106,11 +106,13 @@ public class MybatisInterceptor implements Interceptor { ...@@ -106,11 +106,13 @@ public class MybatisInterceptor implements Interceptor {
Field[] fields = null; Field[] fields = null;
if (parameter instanceof ParamMap) { if (parameter instanceof ParamMap) {
ParamMap<?> p = (ParamMap<?>) parameter; ParamMap<?> p = (ParamMap<?>) parameter;
//update-begin-author:scott date:20190729 for:批量更新报错issues/IZA3Q--
if (p.containsKey("et")) { if (p.containsKey("et")) {
parameter = p.get("et"); parameter = p.get("et");
} else { } else {
parameter = p.get("param1"); parameter = p.get("param1");
} }
//update-end-author:scott date:20190729 for:批量更新报错issues/IZA3Q-
fields = oConvertUtils.getAllFields(parameter); fields = oConvertUtils.getAllFields(parameter);
} else { } else {
fields = oConvertUtils.getAllFields(parameter); fields = oConvertUtils.getAllFields(parameter);
......
package org.jeecg.modules.cas.controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.cas.util.CASServiceUtil;
import org.jeecg.modules.cas.util.XmlUtils;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysDepartService;
import org.jeecg.modules.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
/**
* <p>
* CAS单点登录客户端登录认证
* </p>
*
* @Author zhoujf
* @since 2018-12-20
*/
@Slf4j
@RestController
@RequestMapping("/cas/client")
public class CasClientController {
@Autowired
private ISysUserService sysUserService;
@Autowired
private ISysDepartService sysDepartService;
@Autowired
private RedisUtil redisUtil;
@Value("${cas.prefixUrl}")
private String prefixUrl;
@GetMapping("/validateLogin")
public Object validateLogin(@RequestParam(name="ticket") String ticket,
@RequestParam(name="service") String service,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
Result<JSONObject> result = new Result<JSONObject>();
log.info("Rest api login.");
try {
String validateUrl = prefixUrl+"/p3/serviceValidate";
String res = CASServiceUtil.getSTValidate(validateUrl, ticket, service);
log.info("res."+res);
final String error = XmlUtils.getTextForElement(res, "authenticationFailure");
if(StringUtils.isNotEmpty(error)) {
throw new Exception(error);
}
final String principal = XmlUtils.getTextForElement(res, "user");
if (StringUtils.isEmpty(principal)) {
throw new Exception("No principal was found in the response from the CAS server.");
}
log.info("-------token----username---"+principal);
//1. 校验用户是否有效
SysUser sysUser = sysUserService.getUserByName(principal);
result = sysUserService.checkUserIsEffective(sysUser);
if(!result.isSuccess()) {
return result;
}
String token = JwtUtil.sign(sysUser.getUsername(), sysUser.getPassword());
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
// 设置超时时间
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
// 获取用户部门信息
JSONObject obj = new JSONObject();
List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
obj.put("departs", departs);
if (departs == null || departs.size() == 0) {
obj.put("multi_depart", 0);
} else if (departs.size() == 1) {
sysUserService.updateUserDepart(principal, departs.get(0).getOrgCode());
obj.put("multi_depart", 1);
} else {
obj.put("multi_depart", 2);
}
obj.put("token", token);
obj.put("userInfo", sysUser);
result.setResult(obj);
result.success("登录成功");
} catch (Exception e) {
//e.printStackTrace();
result.error500(e.getMessage());
}
return new HttpEntity<>(result);
}
}
package org.jeecg.modules.cas.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
public class CASServiceUtil {
public static void main(String[] args) {
String serviceUrl = "https://cas.8f8.com.cn:8443/cas/p3/serviceValidate";
String service = "http://localhost:3003/user/login";
String ticket = "ST-5-1g-9cNES6KXNRwq-GuRET103sm0-DESKTOP-VKLS8B3";
String res = getSTValidate(serviceUrl,ticket, service);
System.out.println("---------res-----"+res);
}
/**
* 验证ST
*/
public static String getSTValidate(String url,String st, String service){
try {
url = url+"?service="+service+"&ticket="+st;
CloseableHttpClient httpclient = createHttpClientWithNoSsl();
HttpGet httpget = new HttpGet(url);
HttpResponse response = httpclient.execute(httpget);
String res = readResponse(response);
return res == null ? null : (res == "" ? null : res);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* 读取 response body 内容为字符串
*
* @param response
* @return
* @throws IOException
*/
private static String readResponse(HttpResponse response) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String result = new String();
String line;
while ((line = in.readLine()) != null) {
result += line;
}
return result;
}
/**
* 创建模拟客户端(针对 https 客户端禁用 SSL 验证)
*
* @param cookieStore 缓存的 Cookies 信息
* @return
* @throws Exception
*/
private static CloseableHttpClient createHttpClientWithNoSsl() throws Exception {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// don't check
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// don't check
}
}
};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAllCerts, null);
LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(ctx);
return HttpClients.custom()
.setSSLSocketFactory(sslSocketFactory)
.build();
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -11,6 +11,7 @@ import javax.servlet.http.HttpServletResponse; ...@@ -11,6 +11,7 @@ import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result; import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog; import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.aspect.annotation.PermissionData; import org.jeecg.common.aspect.annotation.PermissionData;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.base.controller.JeecgController; import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator; import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.DateUtils; import org.jeecg.common.util.DateUtils;
...@@ -143,7 +144,8 @@ public class JeecgDemoController extends JeecgController<JeecgDemo,IJeecgDemoSer ...@@ -143,7 +144,8 @@ public class JeecgDemoController extends JeecgController<JeecgDemo,IJeecgDemoSer
*/ */
@PutMapping(value = "/edit") @PutMapping(value = "/edit")
@ApiOperation(value = "编辑DEMO", notes = "编辑DEMO") @ApiOperation(value = "编辑DEMO", notes = "编辑DEMO")
public Result<JeecgDemo> eidt(@RequestBody JeecgDemo jeecgDemo) { @AutoLog(value = "编辑DEMO",operateType= CommonConstant.OPERATE_TYPE_3)
public Result<JeecgDemo> edit(@RequestBody JeecgDemo jeecgDemo) {
Result<JeecgDemo> result = new Result<JeecgDemo>(); Result<JeecgDemo> result = new Result<JeecgDemo>();
JeecgDemo jeecgDemoEntity = jeecgDemoService.getById(jeecgDemo.getId()); JeecgDemo jeecgDemoEntity = jeecgDemoService.getById(jeecgDemo.getId());
if (jeecgDemoEntity == null) { if (jeecgDemoEntity == null) {
......
...@@ -25,7 +25,7 @@ import lombok.experimental.Accessors; ...@@ -25,7 +25,7 @@ import lombok.experimental.Accessors;
public class SysMessage extends JeecgEntity { public class SysMessage extends JeecgEntity {
/**推送内容*/ /**推送内容*/
@Excel(name = "推送内容", width = 15) @Excel(name = "推送内容", width = 15)
private java.lang.Object esContent; private java.lang.String esContent;
/**推送所需参数Json格式*/ /**推送所需参数Json格式*/
@Excel(name = "推送所需参数Json格式", width = 15) @Excel(name = "推送所需参数Json格式", width = 15)
private java.lang.String esParam; private java.lang.String esParam;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment